<?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[RSS Feed]]></title><description><![CDATA[Personal website for portfolio showcase and blog posts]]></description><link>https://www.zuhaibahmad.com</link><generator>GatsbyJS</generator><lastBuildDate>Tue, 24 Feb 2026 11:49:21 GMT</lastBuildDate><item><title><![CDATA[Indie Devlog #00]]></title><description><![CDATA[This June marks 12 years since I built my first app. I have lost count of how many products I have worked on since then. Well into the…]]></description><link>https://www.zuhaibahmad.com/indie-devlog-01/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/indie-devlog-01/</guid><pubDate>Sat, 03 Jan 2026 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;This June marks &lt;strong&gt;12 years&lt;/strong&gt; since I built my first app. I have lost count of how many products I have worked on since then. Well into the triple digits, if I were to guess. Which is why it is surprising that I have never published a &quot;serious&quot; product of my own.&lt;/p&gt;
&lt;p&gt;Being tied to a never-ending stream of projects was the main reason. But I want to change that this year. With the help of AI-assisted development and a much-needed sabbatical from professional work, I finally have the opportunity.&lt;/p&gt;
&lt;p&gt;So, what will I build? Beyond my backlog of ideas and requests collected over the years from friends and family, I will use the time-tested method of &lt;em&gt;scratching your own itch&lt;/em&gt;. As a regular premium user of at least a dozen products, from fitness and nutrition to reading and expense tracking, you naturally spot friction and long-standing issues. The details are a bit fuzzy, but the core theme would be to reduce friction and make the user experience more delightful.&lt;/p&gt;
&lt;p&gt;I will be documenting the journey here and hope to collect feedback and share something valuable along the way. And lastly, a very happy new year.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Git Worktrees: Work on Multiple Flutter Branches Simultaneously]]></title><description><![CDATA[The context-switching tax You're knee-deep in a new feature – state management wired up, widgets looking good, tests passing. Then Slack…]]></description><link>https://www.zuhaibahmad.com/git-worktrees-flutter/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/git-worktrees-flutter/</guid><pubDate>Sat, 16 Aug 2025 10:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/93201ad5604ca6ee52e61b9083cafd29/71c1d/git-worktrees-header.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAACEElEQVR42n2S+2/SUBTH+yeYmAmOV0d72/I0bA4GBmlpS6EQ6IDKY+0ooC0Phzr3cs44N4YPfjAz0xnnD/6p3jK3ELKZfJL7ON9zz805XwR4U8DLQ3CIZ4qZ400guCeFe7gJl1fcFPwt+383iFmBmk3GPLz7ugJ12xNmZY4IiFRQJP1p0i9ACA/rjdT91XMU0CiRrGePFkNl3MddRk2BT7hKJhnyccvmz9+bD1vsMasjdtcaJvxiaf1tIJTzPsg2Wwdugp2zrVgcMYs9CpmzRVHAwDYhcImzysmn89HJ6fHw7Gj4/eu3P1KpsxzJ19TXjdZuaKUgyb2P45+Hw9MPw7Pj0Y/P44t4su7CacSJ0/lVPSNvRsuHaanLZbScpJeUnTt2ulgyipW+5X5cWd+WV3fK+T0YSrCKXB3wYtPhTiAukMxkNSyiEbVfld6ICgjAx/PShnXZYCrvC+LLcFDNS52HS31D/91o786j8aVIkeZUJ0abyXL1+aN0V9DGqrYVWMwleFWubmAk4yQFnEy5KfaJMhD5Qf/Zl0K5SwUzWcnISR0HlkDcFFdZe/XUOKhqezV1c62x1dL3IbClAM6MYuEU2vp+s/1GUbfrE4HeeyfXXsBmIdBhLsBAFgC9AJJwNi6ccWH09DzhD6EAJaCAQcFEgNNmt/GJN69NOtmYTNvwKjQrQGa97f2Pt6dNZnr7L2DTvyzMA6MnAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Git Worktrees: Work on Multiple Flutter Branches Simultaneously&quot;
        title=&quot;&quot;
        src=&quot;/static/93201ad5604ca6ee52e61b9083cafd29/6af66/git-worktrees-header.png&quot;
        srcset=&quot;/static/93201ad5604ca6ee52e61b9083cafd29/69538/git-worktrees-header.png 160w,
/static/93201ad5604ca6ee52e61b9083cafd29/72799/git-worktrees-header.png 320w,
/static/93201ad5604ca6ee52e61b9083cafd29/6af66/git-worktrees-header.png 640w,
/static/93201ad5604ca6ee52e61b9083cafd29/d9199/git-worktrees-header.png 960w,
/static/93201ad5604ca6ee52e61b9083cafd29/21b4d/git-worktrees-header.png 1280w,
/static/93201ad5604ca6ee52e61b9083cafd29/71c1d/git-worktrees-header.png 1536w&quot;
        sizes=&quot;(max-width: 640px) 100vw, 640px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;The context-switching tax&lt;/h2&gt;
&lt;p&gt;You&apos;re knee-deep in a new feature – state management wired up, widgets looking good, tests passing. Then Slack pings: &lt;strong&gt;&quot;Production bug. Needs a hotfix NOW.&quot;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;You know the drill:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;git stash
git checkout main
git checkout -b hotfix/critical-issue
# fix the bug, test it, push it
git checkout feature/new-feature
git stash pop
# pray your IDE catches up&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;By the time you&apos;re back, your IDE is re-indexing, the emulator needs a fresh run, and you&apos;ve lost your train of thought. The context switch costs &lt;strong&gt;15 minutes minimum&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;Worktrees: multiple branches, zero stashing&lt;/h2&gt;
&lt;p&gt;Git worktrees let you check out different branches in &lt;strong&gt;separate directories&lt;/strong&gt; from the same repo. No stashing, no branch switching, no waiting for IDE re-indexing.&lt;/p&gt;
&lt;p&gt;Here&apos;s the setup:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# Your main working directory
~/Code/my_app/

# Create a worktree for the hotfix in a parallel directory
git worktree add ../my_app-hotfix main&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now you have:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;~/Code/my_app/          # Your feature branch (untouched)
~/Code/my_app-hotfix/   # Fresh main branch checkout&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Open the hotfix directory in a &lt;strong&gt;new IDE window&lt;/strong&gt;, fix the bug, commit, push. Your feature branch keeps running in the original window. &lt;strong&gt;Zero interruption.&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;Real-world Flutter scenarios&lt;/h2&gt;
&lt;h3&gt;Scenario 1: Code review while working&lt;/h3&gt;
&lt;p&gt;PM needs you to review a PR but you&apos;re mid-refactor with uncommitted changes everywhere.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;git worktree add ../my_app-review feature/teammate-pr&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Open it, review the code, run the app, leave comments. Your main work stays untouched.&lt;/p&gt;
&lt;h3&gt;Scenario 2: Compare behavior across branches&lt;/h3&gt;
&lt;p&gt;&quot;This button worked on &lt;code&gt;main&lt;/code&gt; but broke on my branch.&quot;&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;git worktree add ../my_app-main main&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Run both apps side-by-side. Instant comparison. No guessing, no stashing.&lt;/p&gt;
&lt;h3&gt;Scenario 3: Long-running experiments&lt;/h3&gt;
&lt;p&gt;You want to try a new architecture pattern but don&apos;t want to commit to it yet.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;git worktree add ../my_app-experiment -b experiment/new-arch&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Work on it for days. If it works, merge it. If not, delete the worktree. Your main branch never saw the chaos.&lt;/p&gt;
&lt;h2&gt;Commands you&apos;ll actually use&lt;/h2&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;# Create a new worktree from an existing branch
git worktree add &amp;lt;path&amp;gt; &amp;lt;branch&amp;gt;

# Create a worktree with a new branch
git worktree add &amp;lt;path&amp;gt; -b &amp;lt;new-branch&amp;gt;

# List all worktrees
git worktree list

# Remove a worktree when you&amp;#39;re done
git worktree remove &amp;lt;path&amp;gt;
# or just delete the directory and run
git worktree prune&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Things to know&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Shared commits&lt;/strong&gt;: All worktrees share the same repository. A commit in one worktree is visible in all others after a &lt;code&gt;git fetch&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;One branch per worktree&lt;/strong&gt;: You can&apos;t check out the same branch in multiple worktrees. Git prevents this to avoid conflicts.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;IDE quirks&lt;/strong&gt;: VS Code and Android Studio handle worktrees fine – just open each as a separate window. IntelliJ IDEA might complain about duplicate projects; ignore it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;pub get runs independently&lt;/strong&gt;: Each worktree needs its own &lt;code&gt;flutter pub get&lt;/code&gt;. They don&apos;t share &lt;code&gt;build&lt;/code&gt; or &lt;code&gt;.dart_tool&lt;/code&gt; folders.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Clean-up&lt;/h2&gt;
&lt;p&gt;When you&apos;re done with a worktree:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;git worktree remove ../my_app-hotfix&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Or just delete the folder and prune:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;rm -rf ../my_app-hotfix
git worktree prune&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Why this matters&lt;/h2&gt;
&lt;p&gt;Before worktrees I avoided urgent fixes because the mental overhead of stashing and switching was too high. Now I keep 2-3 worktrees active:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;my_app&lt;/code&gt; – main feature work&lt;/li&gt;
&lt;li&gt;&lt;code&gt;my_app-hotfix&lt;/code&gt; – quick fixes and reviews&lt;/li&gt;
&lt;li&gt;&lt;code&gt;my_app-experiment&lt;/code&gt; – risky ideas&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I bounce between them &lt;strong&gt;instantly&lt;/strong&gt;. No stashing. No waiting. No lost context.&lt;/p&gt;
&lt;p&gt;If you frequently switch branches or need to compare behavior across branches, worktrees are a game-changer. Try it once and you won&apos;t go back.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Automatically Trigger Dart Test Runs On File Changes]]></title><description><![CDATA[Jest has us spoiled In the JavaScript world I run jest --watch and forget about it. Change a file and the tests rerun before I can switch…]]></description><link>https://www.zuhaibahmad.com/automatically-trigger-dart-test-runs-on-file-changes/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/automatically-trigger-dart-test-runs-on-file-changes/</guid><pubDate>Thu, 26 Jun 2025 07:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d5463c9af075dbd28e43909e7ea6e677/71c1d/automatically-trigger-dart-test-runs-on-file-changes.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAIAAAAmMtkJAAAACXBIWXMAAAsTAAALEwEAmpwYAAADJElEQVR42gEZA+b8AP7Xjv/Yi87Mo/Tal8+1fmhvZXV7a6OZdurIh+zMj+PGkPbeks7SlfzakfmjafWPXfaSYPikbP3Zj/3WjgD/2Yq5yKyEzNO/tIUOKEoBF0UcL0YwRFHnxoXTvI+JjY2ZoYrEw5T62JHxXjjuWEXwfWvwYkH/3ZD+2I0A/9eL49OdfLy8lpZ8NDlIc19P3pJS8LFr/9+R4NqX3NeWwK2MtKWS/NqO+4hf+oly9p6I92xH1caR5M2TAP/ZkM/IjMnGkdPCi4JsVv+hUvKZUu+iXePFh5eWeqqgfLqqf7Cjfsm3hMdqUslXS+ptUt9sScC+jfi9aAD/0oCuuIl/s4j41or8pFf+lknwm1X/yYCVkngIIjccMjwaMj8eN0MQLkMULkEBJEKKgWNWn4E5jXbwq0cA/blQ/548r4ZYL7bDr6N5xqJv08CC/8tkcm9YAyk9HC86Fy46Fy47CyU6CSY7AyE6Z3FpUIyMO4aRvZ5kAP+JKMmHQw6rxwCr0weszACw1Uytpf+vPVRZSgAhQAMbOAAcOgAcOQAcOQUxQwgpP5qKaHWHm1x4ltKiYACcTywOdZYkzewQsc8astE7xecTpsLjslo/TEcAITwCHjkBHzkBHjkCIToDNEQcM0F5bFETPmMQO13dnU0AUUhPAISxDK/SAZ7DEq/QHajRj6SH1IpEFjRKACVHAiRCACI/ACA9AiU/ATFFGio8eXJuf5ukVUdD9nAeAFxRTQCPt6KZc6CVcgCszwmawG5eUBMiPhAtShwySAAnSgEpTAEpTAMrTAAmSygrO3x1cn2bqUdQXOFvKQB+WUQAn864q3z/ok6tqYCMhGhdUEdyYFDql1TujkpAQ0kAIEQIKkkAJUoWJ0CSYTt+Yj99SC2kYTrucSQAn2I8AJ7ApZVt/5E//JRE/pZE/5VE8IpDom9LUU9OGi5GABpAABxDGSpCuGAsSJN6MHdw7Woc/3MZ824dALtrNmN5cMFSHc5YHdpeHd5fHONiG+BhG9VgHsFbI59VLXlKNFZAOsRgJ/9yG6hvO7liLPNsHe9uH/JvHhA+bHKfmcf/AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Automatically Trigger Dart Test Runs On File Changes&quot;
        title=&quot;&quot;
        src=&quot;/static/d5463c9af075dbd28e43909e7ea6e677/6af66/automatically-trigger-dart-test-runs-on-file-changes.png&quot;
        srcset=&quot;/static/d5463c9af075dbd28e43909e7ea6e677/69538/automatically-trigger-dart-test-runs-on-file-changes.png 160w,
/static/d5463c9af075dbd28e43909e7ea6e677/72799/automatically-trigger-dart-test-runs-on-file-changes.png 320w,
/static/d5463c9af075dbd28e43909e7ea6e677/6af66/automatically-trigger-dart-test-runs-on-file-changes.png 640w,
/static/d5463c9af075dbd28e43909e7ea6e677/d9199/automatically-trigger-dart-test-runs-on-file-changes.png 960w,
/static/d5463c9af075dbd28e43909e7ea6e677/21b4d/automatically-trigger-dart-test-runs-on-file-changes.png 1280w,
/static/d5463c9af075dbd28e43909e7ea6e677/71c1d/automatically-trigger-dart-test-runs-on-file-changes.png 1536w&quot;
        sizes=&quot;(max-width: 640px) 100vw, 640px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h3&gt;Jest has us spoiled&lt;/h3&gt;
&lt;p&gt;In the JavaScript world I run &lt;code&gt;jest --watch&lt;/code&gt; and forget about it. Change a file and the tests rerun before I can switch windows. It makes TDD painless.&lt;/p&gt;
&lt;h3&gt;Flutter? Crickets&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;flutter test&lt;/code&gt; is fine &lt;strong&gt;but&lt;/strong&gt; it has no built-in watcher. I searched for plug-ins, flags, secret env vars – nothing. There are a handful of community tools but most are abandoned.&lt;/p&gt;
&lt;h3&gt;Remember the &lt;code&gt;spec&lt;/code&gt; package?&lt;/h3&gt;
&lt;p&gt;For a while &lt;a href=&quot;https://pub.dev/packages/spec&quot;&gt;&lt;code&gt;spec&lt;/code&gt;&lt;/a&gt; wrapped the test runner and watched your files. Sadly it stopped working after the Dart 3 upgrade and the repo has been dormant.&lt;/p&gt;
&lt;h3&gt;I even tried Nodemon&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;nodemon --exec &quot;flutter test&quot; --ext dart&lt;/code&gt; almost works, until it doesn&apos;t. It gets confused by generated files, spams multiple runs and never exits cleanly on Ctrl-C. After the third flaky loop I gave up.&lt;/p&gt;
&lt;h3&gt;Enter &lt;code&gt;fswatch&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;fswatch&lt;/code&gt; is a tiny cross-platform file watcher written in C. Homebrew users can grab it with:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;brew install fswatch&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Then add a shell alias. I dropped this in my &lt;code&gt;~/.zshrc&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;alias dtest=&amp;#39;fswatch -or lib test | xargs -n1 -I{} flutter test&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Breakdown:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;fswatch -or lib test&lt;/code&gt; – recursively watch the &lt;code&gt;lib&lt;/code&gt; and &lt;code&gt;test&lt;/code&gt; folders and emit one event (&lt;code&gt;-o&lt;/code&gt;) each time something changes.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;xargs -n1 -I{} flutter test&lt;/code&gt; – for every event run &lt;strong&gt;one&lt;/strong&gt; &lt;code&gt;flutter test&lt;/code&gt; run.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Open a new terminal tab, run &lt;code&gt;dtest&lt;/code&gt;, start coding and watch the green ticks fly. Zero config, instant feedback.&lt;/p&gt;
&lt;p&gt;That&apos;s it. No more manual &lt;code&gt;flutter test&lt;/code&gt; spam, no flaky wrappers. Just a simple watcher that does one job and does it well.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Lazy Loading Libraries in Dart]]></title><description><![CDATA[When it comes to building large scale applications, efficient use of resources is one of the most important considerations. This includes…]]></description><link>https://www.zuhaibahmad.com/dart-deferred-keyword/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/dart-deferred-keyword/</guid><pubDate>Sun, 21 May 2023 08:25:00 GMT</pubDate><content:encoded>&lt;p&gt;When it comes to building large scale applications, efficient use of resources is one of the most important considerations. This includes not only memory usage but also network bandwidth, disk space, and CPU cycles. One way of achieving this is through lazy loading.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lazy loading&lt;/strong&gt; refers to loading code or resources only when they are actually needed. This approach can greatly reduce the size and complexity of an application by loading only the necessary parts, thus improving startup time, reducing network bandwidth, and reducing memory usage.&lt;/p&gt;
&lt;p&gt;In Dart, the &lt;code&gt;deferred&lt;/code&gt; keyword is used to implement lazy loading of libraries. This keyword allows you to specify that a library should be loaded only when needed, rather than being loaded when the application starts up.&lt;/p&gt;
&lt;h2&gt;How to Use Deferred Loading in Dart&lt;/h2&gt;
&lt;p&gt;To use deferred loading in Dart, you need to follow a few simple steps.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Declare the library as &lt;code&gt;deferred&lt;/code&gt;&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import &amp;#39;package:deferred_library/deferred_library.dart&amp;#39; deferred as deferred_library;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Use the library when needed&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;someFunction() async {
    await deferred_library.loadLibrary();
    deferred_library.someFunctionInLibrary();
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;In this example, the &lt;code&gt;loadLibrary&lt;/code&gt; method is called before accessing any function defined in the &lt;code&gt;deferred_library&lt;/code&gt;. This method loads the library if it has not already been loaded. Once the library is loaded, any function or variable defined in it can be used normally.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Advantages of Using Deferred Loading&lt;/h2&gt;
&lt;p&gt;Using deferred loading in your Dart application provides several advantages:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced application loading time:&lt;/strong&gt; By loading only the necessary code when it is actually needed, the application startup time can be greatly reduced.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduced memory usage:&lt;/strong&gt; If a library is not used frequently, it is wasteful to keep it in memory all the time. Deferred loading ensures that the library is loaded only when needed, thus reducing memory usage.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Better network bandwidth usage:&lt;/strong&gt; If the library is loaded lazily, the initial download size of the application can be reduced, resulting in faster downloads and better network bandwidth usage.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Lazy loading is a crucial technique for optimizing resource usage in large-scale applications. Dart has a built-in capability to load libraries lazily using the &lt;code&gt;deferred&lt;/code&gt; keyword. This allows you to load only the necessary parts of the application, thus reducing memory usage, network bandwidth, and startup time. By using this technique judiciously, you can greatly improve the performance of your Dart application.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Building a ChatGPT App using Flutter]]></title><description><![CDATA[Flutter is a state-of-the-art cross-platform mobile development framework owned by Google that allows developers to create visually stunning…]]></description><link>https://www.zuhaibahmad.com/flutter-chatgpt/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/flutter-chatgpt/</guid><pubDate>Sun, 05 Mar 2023 06:49:00 GMT</pubDate><content:encoded>&lt;p&gt;Flutter is a state-of-the-art cross-platform mobile development framework owned by Google that allows developers to create visually stunning, high-performance native applications for iOS, Android, and web. On the other hand, ChatGPT is an AI model that utilizes the power of GPT to generate human-like responses to chat messages.&lt;/p&gt;
&lt;p&gt;In this tutorial, we&apos;ll be putting both technologies together to create a ChatGPT application using Flutter. We&apos;ll be utilizing the Dart programming language and Flutter&apos;s widgets to communicate with ChatGPT and display the chat interface.&lt;/p&gt;
&lt;h2&gt;Prerequisites&lt;/h2&gt;
&lt;p&gt;Before we start building our application, you&apos;ll need to set up your development environment. Here are the necessary prerequisites:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://flutter.dev/docs/get-started/install&quot;&gt;Flutter SDK&lt;/a&gt; installed on your machine.&lt;/li&gt;
&lt;li&gt;An IDE, such as Android Studio, IntelliJ IDEA, or Visual Studio Code.&lt;/li&gt;
&lt;li&gt;A working knowledge of Flutter and Dart.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Setting up the Application&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;Create a new project in your IDE of choice.&lt;/li&gt;
&lt;li&gt;Add the &lt;code&gt;http&lt;/code&gt; package and the &lt;code&gt;flutter_dotenv&lt;/code&gt; package to your &lt;code&gt;pubspec.yaml&lt;/code&gt; file using the &lt;code&gt;dependencies&lt;/code&gt; property.&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;yaml&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;dependencies:
  flutter:
    sdk: flutter
  http: ^0.12.2
  flutter_dotenv: ^4.0.0&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;3&quot;&gt;
&lt;li&gt;Create a &lt;code&gt;.env&lt;/code&gt; file in your project&apos;s root directory and add your ChatGPT API key.&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;OPENAI_API_KEY=&amp;lt;YOUR_API_KEY&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;4&quot;&gt;
&lt;li&gt;Import the necessary packages in your &lt;code&gt;main.dart&lt;/code&gt; file.&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import &amp;#39;dart:convert&amp;#39;;
import &amp;#39;package:flutter/material.dart&amp;#39;;
import &amp;#39;package:http/http.dart&amp;#39; as http;
import &amp;#39;package:flutter_dotenv/flutter_dotenv.dart&amp;#39;;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;5&quot;&gt;
&lt;li&gt;Create a &lt;code&gt;ChatPage&lt;/code&gt; class that will display the chat interface and handle the user&apos;s input.&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;class ChatPage extends StatefulWidget {
  const ChatPage({super.key});

  @override
  ChatPageState createState() =&amp;gt; ChatPageState();
}

class ChatPageState extends State&amp;lt;ChatPage&amp;gt; {
  // Map of messages and whether they were sent by the user
  final _messages = &amp;lt;String, bool&amp;gt;{};
  final _textEditingController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text(&amp;#39;ChatGPT Demo&amp;#39;),
      ),
      body: Column(
        children: [
          Expanded(
            child: ListView.builder(
              itemBuilder: (context, index) {
                var isUserMessage = _messages.values.elementAt(index);
                return Row(
                  mainAxisAlignment: isUserMessage
                      ? MainAxisAlignment.end
                      : MainAxisAlignment.start,
                  children: [
                    Flexible(
                      child: Container(
                        alignment: isUserMessage
                            ? Alignment.centerRight
                            : Alignment.centerLeft,
                        padding: const EdgeInsets.all(16),
                        margin: EdgeInsets.only(
                          top: 4,
                          bottom: 4,
                          left: isUserMessage ? 40 : 4,
                          right: !isUserMessage ? 40 : 4,
                        ),
                        decoration: BoxDecoration(
                          borderRadius: BorderRadius.circular(8),
                          color: isUserMessage ? Colors.blue : Colors.grey,
                        ),
                        child: Text(_messages.keys.elementAt(index)),
                      ),
                    ),
                  ],
                );
              },
              itemCount: _messages.length,
            ),
          ),
          TextField(
            controller: _textEditingController,
            onSubmitted: _sendMessage,
            decoration: InputDecoration(
              hintText: &amp;#39;Type your message&amp;#39;,
              contentPadding: const EdgeInsets.all(8),
              suffixIcon: IconButton(
                icon: const Icon(Icons.send),
                onPressed: () =&amp;gt; _sendMessage(_textEditingController.text),
              ),
            ),
          ),
        ],
      ),
    );
  }

  void _sendMessage(String message) async {
    setState(() {
      _messages[message] = true;
      _messages[&amp;quot;...&amp;quot;] = false;
    });

    // Send API request to ChatGPT
    final response = await http.post(
      Uri.parse(&amp;#39;https://api.openai.com/v1/chat/completions&amp;#39;),
      headers: {
        &amp;#39;Authorization&amp;#39;: &amp;#39;Bearer $openAiKey&amp;#39;,
        &amp;#39;Content-Type&amp;#39;: &amp;#39;application/json&amp;#39;,
      },
      body: jsonEncode(&amp;lt;String, dynamic&amp;gt;{
        &amp;#39;model&amp;#39;: &amp;#39;gpt-3.5-turbo&amp;#39;,
        &amp;quot;messages&amp;quot;: [
          {&amp;quot;role&amp;quot;: &amp;quot;user&amp;quot;, &amp;quot;content&amp;quot;: message}
        ]
      }),
    );

    if (response.statusCode == 200) {
      final json = jsonDecode(response.body);
      setState(() {
        _messages.remove(&amp;quot;...&amp;quot;);
        _messages[json[&amp;#39;choices&amp;#39;][0][&amp;#39;message&amp;#39;][&amp;#39;content&amp;#39;]] = false;
      });
    } else {
      print(&amp;#39;Request failed with status: ${response.statusCode}.&amp;#39;);
    }

    // Clear text field
    _textEditingController.clear();
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;6&quot;&gt;
&lt;li&gt;Modify your &lt;code&gt;main.dart&lt;/code&gt; file to display the &lt;code&gt;ChatPage&lt;/code&gt; when the app starts.&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;final openAiKey = dotenv.env[&amp;#39;OPENAI_API_KEY&amp;#39;];

void main() async {
  await dotenv.load();
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &amp;#39;ChatGPT Demo&amp;#39;,
      home: ChatPage(),
    );
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;That&apos;s it! You now have a fully functional ChatGPT application built with Flutter. Launch your app and start chatting with ChatGPT.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;/c58746e8b6afb08a5d0caa335744baf6/flutter-gpt-demo.gif&quot; alt=&quot;ChatGPT App Demo&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Build Modern Command Line Interfaces (CLI) with Python]]></title><description><![CDATA[When it comes to command-line interfaces (CLI), they are often viewed as dull and unengaging. However, CLI wizards offer a fun and…]]></description><link>https://www.zuhaibahmad.com/building-cli-with-python/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/building-cli-with-python/</guid><pubDate>Sun, 29 Jan 2023 00:33:00 GMT</pubDate><content:encoded>&lt;p&gt;When it comes to command-line interfaces (CLI), they are often viewed as dull and unengaging. However, CLI wizards offer a fun and interactive way for users to interact with the CLI. In this post, we&apos;ll take a deep dive into how to create a beautiful CLI wizard using PyInquirer.&lt;/p&gt;
&lt;h2&gt;What is PyInquirer?&lt;/h2&gt;
&lt;p&gt;PyInquirer is an interactive CLI prompt library that allows you to build interactive command-line interfaces. The library provides intuitive prompts with various controls and widgets, such as dropdowns, checkboxes, text input, and more. The library is built on top of the prompt-toolkit library for Python.&lt;/p&gt;
&lt;h2&gt;How to Install PyInquirer&lt;/h2&gt;
&lt;p&gt;To install PyInquirer, open your terminal and run the following command:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;pip install PyInquirer&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Creating a CLI Wizard with PyInquirer&lt;/h2&gt;
&lt;p&gt;To create a CLI wizard using PyInquirer, you need to first define the questions to ask the user. In PyInquirer, these questions are referred to as prompts. The library supports various types of prompts, including input prompts, list prompts, checkbox prompts, and more.&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of a simple CLI wizard using PyInquirer:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;from PyInquirer import prompt

questions = [
    {
        &amp;#39;type&amp;#39;: &amp;#39;input&amp;#39;,
        &amp;#39;name&amp;#39;: &amp;#39;name&amp;#39;,
        &amp;#39;message&amp;#39;: &amp;#39;What is your name?&amp;#39;
    },
    {
        &amp;#39;type&amp;#39;: &amp;#39;checkbox&amp;#39;,
        &amp;#39;message&amp;#39;: &amp;#39;Select toppings&amp;#39;,
        &amp;#39;name&amp;#39;: &amp;#39;toppings&amp;#39;,
        &amp;#39;choices&amp;#39;: [
            {&amp;#39;name&amp;#39;: &amp;#39;Pepperoni&amp;#39;},
            {&amp;#39;name&amp;#39;: &amp;#39;Mushroom&amp;#39;},
            {&amp;#39;name&amp;#39;: &amp;#39;Onion&amp;#39;},
            {&amp;#39;name&amp;#39;: &amp;#39;Tomato&amp;#39;},
            {&amp;#39;name&amp;#39;: &amp;#39;Extra cheese&amp;#39;},
        ],
        &amp;#39;validate&amp;#39;: lambda answer: &amp;#39;You must choose at least one topping.&amp;#39; \
            if len(answer) == 0 else True
    }
]

answers = prompt(questions)
print(answers)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;In this example, we define two prompts: one for the user&apos;s name and another for the pizza toppings they want. The prompts are defined using a Python dictionary, with each prompt having a name, type, and message.&lt;/p&gt;
&lt;p&gt;We use the &lt;code&gt;prompt&lt;/code&gt; method to display the prompts and get the user&apos;s answers. The method returns a dictionary containing the answers provided by the user.&lt;/p&gt;
&lt;h2&gt;Customizing the Look of the CLI Wizard&lt;/h2&gt;
&lt;p&gt;pyinquirer provides several customization options that allow you to tweak the look and feel of the CLI wizard. You can change the color of the prompts, change the borders, add headers and footers, and more.&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of a custom CLI wizard using pyinquirer:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;from PyInquirer import prompt, style_from_dict, Token

style = style_from_dict({
    Token.Separator: &amp;#39;#cc5454&amp;#39;,
    Token.QuestionMark: &amp;#39;#7f7fff bold&amp;#39;,
    Token.Selected: &amp;#39;#5F819D&amp;#39;,  # default
    Token.Pointer: &amp;#39;#5F819D bold&amp;#39;,
    Token.Instruction: &amp;#39;&amp;#39;,  # default
    Token.Answer: &amp;#39;#5F819D bold&amp;#39;,
    Token.Question: &amp;#39;&amp;#39;,
})

questions = [
    {
        &amp;#39;type&amp;#39;: &amp;#39;list&amp;#39;,
        &amp;#39;name&amp;#39;: &amp;#39;theme&amp;#39;,
        &amp;#39;message&amp;#39;: &amp;#39;What do you want to do?&amp;#39;,
        &amp;#39;choices&amp;#39;: [
            &amp;#39;Create a new project&amp;#39;,
            &amp;#39;Help&amp;#39;,
            &amp;#39;Exit&amp;#39;,
        ],
        &amp;#39;default&amp;#39;: &amp;#39;Create a new project&amp;#39;,
        &amp;#39;filter&amp;#39;: lambda val: val.lower()
    },
    {
        &amp;#39;type&amp;#39;: &amp;#39;input&amp;#39;,
        &amp;#39;name&amp;#39;: &amp;#39;project_name&amp;#39;,
        &amp;#39;message&amp;#39;: &amp;#39;Project name:&amp;#39;,
        &amp;#39;validate&amp;#39;: lambda answer: &amp;#39;You must enter a project name.&amp;#39; \
            if len(answer) == 0 else True
    },
]

answers = prompt(questions, style=style)
print(answers)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;In this example, we define a custom style for the CLI wizard using a Python dictionary. We then pass the style to the &lt;code&gt;prompt&lt;/code&gt; method to apply it to the prompts.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/528a9a6fc3acabd6535db74e057d0c4c/ad997/python-cli-output.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAAB30lEQVR42oVT2ZKbMBDkP7gFNoeQOMRhMGsDZuMjduL9/4/pDKQ2VfsQ70PXTFGoNd090pw4gZsIODGHHcVweAKLakQIAoYwtBDHNizLhGn+hWVZ/4W27SWiTiFv9+imCdXhgOl6wziM4DJAzBkRMjjMg+tt4LjsH7FhGCs++6Vqz+cdp3HA7XLB9XJGuz/iev/AYb6hOV7RDHck+Q6yqJGW3Vpd14Hv+wi2W2wJS79Uxhi0ZtchESm4kCjKEgFZkBYlClWiqitEPEKWCVRViTyThJQOB0jTlL5VKIoCUkqUdDYMQ2hGQR5WBXwiCasa4a4F71o0dU0XuRDCpMMWFF2glMBmo5M8a5Wo6/qKz36p2uHSoz22OJ1mnEl2fxjw+P2Bcf6BKFVg/gYG/WxSKMaXUGzY9lesoTyuP9G33erjnqokkuN0xq4fSXJLaXMkiVxlKaUoIP46ZYe8W2DTujBaH5d6JgQkIREbInOQ5z55VKGuU3ieTVO+IIzeKJC9gtq/oSfZzTDg/fHANM3gaYwwcsk3kwyPaFpaG+ebPXw+f5HccV2b93mmaRS6XUPJZhBZRWQhrQlbPdN14yXZSpjnOQKKO46XaSKS5EOQX5xzZKmk1xKs8D3v21eyEP4BlP5I0otj17cAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Custom CLI Wizard&quot;
        title=&quot;&quot;
        src=&quot;/static/528a9a6fc3acabd6535db74e057d0c4c/6af66/python-cli-output.png&quot;
        srcset=&quot;/static/528a9a6fc3acabd6535db74e057d0c4c/69538/python-cli-output.png 160w,
/static/528a9a6fc3acabd6535db74e057d0c4c/72799/python-cli-output.png 320w,
/static/528a9a6fc3acabd6535db74e057d0c4c/6af66/python-cli-output.png 640w,
/static/528a9a6fc3acabd6535db74e057d0c4c/d9199/python-cli-output.png 960w,
/static/528a9a6fc3acabd6535db74e057d0c4c/ad997/python-cli-output.png 1012w&quot;
        sizes=&quot;(max-width: 640px) 100vw, 640px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;Creating interactive CLI wizards is a great way to engage users and provide a more enjoyable experience when using the CLI. pyinquirer is an easy-to-use library that allows you to create beautiful and functional CLI wizards in Python. With pyinquirer, you can customize the prompts to fit your needs, and with a little creativity, you can create a unique CLI wizard that stands out.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Securing Sensitive Data using Gradle Files]]></title><description><![CDATA[When developing an Android application, it's important to keep sensitive data such as API keys secure. One technique for doing this is to…]]></description><link>https://www.zuhaibahmad.com/sensitive-data-android/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/sensitive-data-android/</guid><pubDate>Mon, 02 Jan 2023 05:09:00 GMT</pubDate><content:encoded>&lt;p&gt;When developing an Android application, it&apos;s important to keep sensitive data such as API keys secure. One technique for doing this is to store the data in an external gradle file.&lt;/p&gt;
&lt;h2&gt;Storing Secrets in a Properties File&lt;/h2&gt;
&lt;p&gt;A &lt;code&gt;Properties&lt;/code&gt; object can be used to load key-value pairs from a file. This allows you to store sensitive data such as API keys and passwords in a separate file that can be excluded from version control.&lt;/p&gt;
&lt;p&gt;Here&apos;s an example of how to load secrets from a &lt;code&gt;secrets.properties&lt;/code&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;groovy&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;def secretsPropertiesFile = rootProject.file(&amp;quot;secrets.properties&amp;quot;)
def secretProperties = new Properties()

if (secretsPropertiesFile.exists()) {
    secretProperties.load(new FileInputStream(secretsPropertiesFile))
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Using Environment Variables&lt;/h2&gt;
&lt;p&gt;If the secrets.properties file does not exist, you can use environment variables as a fallback. This is useful when building your application on a continuous integration server where you may not have access to the secrets.properties file.&lt;/p&gt;
&lt;p&gt;Here’s an example of how to read an environment variable named API_KEY and set it as a property:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;groovy&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;if (!secretsPropertiesFile.exists()) {
    secretProperties.setProperty(&amp;quot;API_KEY&amp;quot;, &amp;quot;${System.getenv(&amp;#39;API_KEY&amp;#39;)}&amp;quot;)
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Setting Build Config Fields&lt;/h2&gt;
&lt;p&gt;Once you have loaded your secrets into a Properties object, you can use them to set build config fields. This allows you to access the values at runtime using the generated &lt;code&gt;BuildConfig&lt;/code&gt; class.&lt;/p&gt;
&lt;p&gt;Here’s an example of how to set two build config fields for &lt;code&gt;API_BASE_URL&lt;/code&gt; and &lt;code&gt;API_KEY&lt;/code&gt;, using values from the previously loaded properties:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;groovy&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;android {
    defaultConfig {
        buildConfigField &amp;quot;String&amp;quot;, &amp;quot;API_BASE_URL&amp;quot;, &amp;quot;\&amp;quot;${secretProperties[&amp;#39;API_BASE_URL&amp;#39;]}\&amp;quot;&amp;quot;
        buildConfigField &amp;quot;String&amp;quot;, &amp;quot;API_KEY&amp;quot;, &amp;quot;\&amp;quot;${secretProperties[&amp;#39;API_KEY&amp;#39;]}\&amp;quot;&amp;quot;
    }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Storing sensitive data in a separate properties file or using environment variables are effective techniques for keeping your data secure. By setting build config fields with these values, you can easily access them at runtime while keeping them out of version control.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Flutter Plugin Development]]></title><description><![CDATA[One of the great things about Flutter is its extensibility through plugins. Even though I have been developing Flutter apps for about…]]></description><link>https://www.zuhaibahmad.com/flutter-plugin-development/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/flutter-plugin-development/</guid><pubDate>Sat, 22 Oct 2022 02:06:00 GMT</pubDate><content:encoded>&lt;p&gt;One of the great things about Flutter is its extensibility through plugins. Even though I have been developing Flutter apps for about 4 years now, my first experience with building Flutter plugins was just last month. In this tutorial, lets walk you through the process of creating your own Flutter plugin.&lt;/p&gt;
&lt;h2&gt;What is a Flutter Plugin?&lt;/h2&gt;
&lt;p&gt;A Flutter plugin is a package that adds new functionality to the Flutter framework. Plugins can access platform-specific APIs (such as camera or location services) and expose them to Dart code in a way that&apos;s easy to use.&lt;/p&gt;
&lt;h2&gt;Getting Started&lt;/h2&gt;
&lt;p&gt;To get started with creating your own Flutter plugin, you&apos;ll need to have the following installed on your computer:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The latest version of &lt;a href=&quot;https://flutter.dev/docs/get-started/install&quot;&gt;Flutter&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;A text editor (such as &lt;a href=&quot;https://code.visualstudio.com/&quot;&gt;VS Code&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;A terminal or command prompt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once you have these installed, open up your terminal or command prompt and navigate to the directory where you want to create your new plugin.&lt;/p&gt;
&lt;h2&gt;Creating a New Plugin&lt;/h2&gt;
&lt;p&gt;To create a new Flutter plugin, run the following command:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sh&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;flutter create --template=plugin my_plugin&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This will create a new directory called &lt;code&gt;my_plugin&lt;/code&gt; with all the necessary files for creating a Flutter plugin.&lt;/p&gt;
&lt;h2&gt;Adding Platform-Specific Code&lt;/h2&gt;
&lt;p&gt;One of the main reasons for creating a Flutter plugin is to access platform-specific APIs. To do this, you&apos;ll need to add some native code (either Java/Kotlin for Android or Objective-C/Swift for iOS) to your plugin.&lt;/p&gt;
&lt;p&gt;For example, let&apos;s say we want our plugin to access the device&apos;s camera. On Android, we would add some Java code like this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// my_plugin/android/src/main/java/com/example/my_plugin/MyPlugin.java
package com.example.my_plugin;

import androidx.annotation.NonNull;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.MethodChannel;

public class MyPlugin implements FlutterPlugin {
  private static final String CHANNEL = &amp;quot;my_plugin&amp;quot;;

  @Override
  public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
    MethodChannel channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), CHANNEL);
    channel.setMethodCallHandler(
      (call, result) -&amp;gt; {
        if (call.method.equals(&amp;quot;getCamera&amp;quot;)) {
          // TODO: Access camera here
        } else {
          result.notImplemented();
        }
      }
    );
  }

  @Override
  public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {}
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;On iOS, we would add some Objective-C code like this&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;swift&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// my_plugin/ios/Classes/SwiftMyPlugin.swift
import Foundation
import UIKit

public class SwiftMyPlugin: NSObject {
    public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: &amp;quot;my_plugin&amp;quot;, binaryMessenger: registrar.messenger())
        let instance = SwiftMyPlugin()
        registrar.addMethodCallDelegate(instance, channel: channel)
    }

    public func handle(_ call: FlutterMethodCall,
                       result: @escaping (Any?) -&amp;gt; Void) {
        if call.method == &amp;quot;getCamera&amp;quot; {
            // TODO: Access camera here
            result(nil)
        } else {
            result(FlutterMethodNotImplemented)
        }
    }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Once you’ve added your platform-specific code, you can expose it to Dart by adding some Dart code like this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// my_plugin/lib/my_plugin.dart
import &amp;#39;dart:async&amp;#39;;

import &amp;#39;package:flutter/services.dart&amp;#39;;

class MyPlugin {
  static const MethodChannel _channel =
      const MethodChannel(&amp;#39;my_plugin&amp;#39;);

static Future&amp;lt;String&amp;gt; get camera async {
    final String version = await _channel.invokeMethod(&amp;#39;getCamera&amp;#39;);
    return version;
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Testing Your Plugin&lt;/h2&gt;
&lt;p&gt;To test your plugin, you can create an example app within your my_plugin directory by running the following command:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;sh&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;cd example/
flutter run&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This will launch an example app that uses your newly created plugin. You can then test out any functionality that you’ve added.&lt;/p&gt;
&lt;h3&gt;Publishing Your Plugin&lt;/h3&gt;
&lt;p&gt;Once you’re happy with your plugin and have tested it thoroughly, it’s time to publish it so others can use it! To do this, follow these steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Update the &lt;code&gt;pubspec.yaml&lt;/code&gt; file in your &lt;code&gt;my_plugin&lt;/code&gt; directory with information about your plugin (such as its name and description).&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;flutter packages pub publish&lt;/code&gt; from within your &lt;code&gt;my_plugin&lt;/code&gt; directory.&lt;/li&gt;
&lt;li&gt;Follow the prompts to publish your package.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Congratulations! You’ve now created and published your very own Flutter plugin!&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In this tutorial, we walked through the process of creating a Flutter plugin from scratch. We covered everything from setting up our development environment to adding platform-specific code and publishing our finished product. We hope this tutorial has been helpful in getting you started with creating your own plugins for use in future projects&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Generate ASCII Art with Python]]></title><description><![CDATA[ASCII art is a form of art that was popularized in the early days of computing, where characters from the ASCII character set are used to…]]></description><link>https://www.zuhaibahmad.com/ascii-art-with-python/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/ascii-art-with-python/</guid><pubDate>Fri, 08 Jul 2022 12:37:00 GMT</pubDate><content:encoded>&lt;p&gt;ASCII art is a form of art that was popularized in the early days of computing, where characters from the ASCII character set are used to create images. It’s a fun and creative way to use programming to create something visually interesting.&lt;/p&gt;
&lt;p&gt;In this post, we will explore how to generate ASCII art using Python.&lt;/p&gt;
&lt;h2&gt;Setting Up the Environment&lt;/h2&gt;
&lt;p&gt;Before we start generating ASCII art, we need to set up our environment. We will be using a Python library called &lt;code&gt;art&lt;/code&gt; which provides functionality for creating ASCII art.&lt;/p&gt;
&lt;p&gt;To install &lt;code&gt;art&lt;/code&gt;, simply run the following command in your terminal:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;pip install art&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Generating ASCII Art&lt;/h2&gt;
&lt;p&gt;Once we have &lt;code&gt;art&lt;/code&gt; installed, we can start generating ASCII art. The library provides a variety of styles and fonts to choose from, and we can also create our own custom fonts.&lt;/p&gt;
&lt;p&gt;Here’s a simple example that generates ASCII art of the word “Python” using the “slant” font:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;from art import text2art

art = text2art(&amp;quot;Python&amp;quot;, font=&amp;#39;slant&amp;#39;)
print(art)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This code will generate the following ASCII art:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;bash&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;    _____       _     ____        _    
   / ___/____  (_)___/ / / ___    (_)___
   \__ \/ __ \/ / __  / / / _ \  / / __ \
  ___/ / / / / / /_/ / /_/  __/ / / /_/ /
 /____/_/ /_/_/\__,_/\__,_/\___(_)_/\____/&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Creating Custom Fonts&lt;/h2&gt;
&lt;p&gt;One of the really cool things about the &lt;code&gt;art&lt;/code&gt; library is that we can create our own custom fonts. This allows us to create ASCII art that is unique and personalized.&lt;/p&gt;
&lt;p&gt;Here’s an example of how to create a custom font:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;from art import tprint, font_creator

font = font_creator(&amp;quot;myfont&amp;quot;,
                r&amp;quot;&amp;quot;&amp;quot;
 ___________________________________________________
/ This is my custom font. It includes all letters   \
| of the alphabet as well as some special characters. |
\ Enjoy!                                             /
 ---------------------------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
&amp;quot;&amp;quot;&amp;quot;)

tprint(&amp;quot;Hello, World!&amp;quot;, font=&amp;#39;myfont&amp;#39;)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This code will create a custom font that includes all letters of the alphabet as well as some special characters. We can then use this font to generate ASCII art using the &lt;code&gt;tprint&lt;/code&gt; function.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Clutterfree Flutter Architecture]]></title><description><![CDATA[Flutter is a modern and popular framework for building mobile applications. While its easy-to-learn syntax and fast development time make it…]]></description><link>https://www.zuhaibahmad.com/clutterfree-flutter-architecture/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/clutterfree-flutter-architecture/</guid><pubDate>Fri, 19 Feb 2021 04:42:00 GMT</pubDate><content:encoded>&lt;p&gt;Flutter is a modern and popular framework for building mobile applications. While its easy-to-learn syntax and fast development time make it a great choice for developing mobile apps, many developers struggle with finding the right architecture that fits their needs. To help solve this problem, we will be discussing a clutter-free architecture setup using a combination of popular libraries such as &lt;a href=&quot;https://pub.dev/packages/bloc&quot;&gt;Bloc&lt;/a&gt;, &lt;a href=&quot;https://pub.dev/packages/chopper&quot;&gt;Chopper&lt;/a&gt;, &lt;a href=&quot;https://pub.dev/packages/auto_route&quot;&gt;Auto Route&lt;/a&gt;, &lt;a href=&quot;https://pub.dev/packages/injectable&quot;&gt;Injectable&lt;/a&gt;, &lt;a href=&quot;https://pub.dev/packages/freezed&quot;&gt;Freezed&lt;/a&gt;, and &lt;a href=&quot;https://pub.dev/packages/flutter_screenutil&quot;&gt;Screen Util&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We will start by building a simple vanilla Flutter application that has basic functionality such as displaying a list of items and navigating to a detail page.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;I will be skipping some of the non-essential code to keep things concise.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import &amp;#39;package:flutter/material.dart&amp;#39;;
import &amp;#39;package:http/http.dart&amp;#39; as http;
import &amp;#39;dart:convert&amp;#39;;

void main() =&amp;gt; runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: &amp;#39;Clutterfree Flutter&amp;#39;,
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  _HomePageState createState() =&amp;gt; _HomePageState();
}

class _HomePageState extends State&amp;lt;HomePage&amp;gt; {
  List&amp;lt;String&amp;gt; words = [];

  @override
  void initState() {
    super.initState();
    fetchWords();
  }

  void fetchWords() async {
    var response = await http.get(&amp;#39;https://random-word-api.herokuapp.com/word?number=10&amp;#39;);
    if (response.statusCode == 200) {
      setState(() {
        words = json.decode(response.body);
      });
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(&amp;#39;Clutterfree Flutter&amp;#39;),
      ),
      body: ListView.builder(
        itemCount: words.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(words[index]),
            onTap: () {
              Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) =&amp;gt; DetailPage(word: words[index]),
                ),
              );
            },
          );
        },
      ),
    );
  }
}

class DetailPage extends StatelessWidget {
  final String word;

  DetailPage({this.word});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(word),
      ),
      body: Center(
        child: Text(word),
      ),
    );
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;With this code, we have a basic two-page Flutter application that makes API calls and displays a list of data on the first page and the details of a selected item on the second page. However, as the application grows in complexity and size, it becomes increasingly difficult to maintain the codebase, especially when it comes to state management and API calls.&lt;/p&gt;
&lt;h2&gt;Write Code Not Boilerplate&lt;/h2&gt;
&lt;p&gt;This is where Flutter libraries such as &lt;a href=&quot;https://pub.dev/packages/bloc&quot;&gt;Bloc&lt;/a&gt;, &lt;a href=&quot;https://pub.dev/packages/chopper&quot;&gt;Chopper&lt;/a&gt;, &lt;a href=&quot;https://pub.dev/packages/auto_route&quot;&gt;Auto Route&lt;/a&gt;, &lt;a href=&quot;https://pub.dev/packages/injectable&quot;&gt;Injectable&lt;/a&gt;, &lt;a href=&quot;https://pub.dev/packages/freezed&quot;&gt;Freezed&lt;/a&gt;, and &lt;a href=&quot;https://pub.dev/packages/flutter_screenutil&quot;&gt;Screen Util&lt;/a&gt; come in. These libraries are designed to help you write scalable, maintainable, and efficient Flutter applications. Let&apos;s take a look at how we can refactor our previous application to make use of these libraries.&lt;/p&gt;
&lt;h3&gt;Freezed&lt;/h3&gt;
&lt;p&gt;Freezed is a code generation library for Dart that provides a set of annotations and code generators to make it easy to create immutable classes and data structures. Freezed makes it simple to create classes that are easy to understand and test, and that can be used throughout your application without having to worry about mutability.&lt;/p&gt;
&lt;h3&gt;BLoC&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://pub.dev/packages/bloc&quot;&gt;Bloc&lt;/a&gt; library provides a powerful and flexible pattern for state management. With &lt;a href=&quot;https://pub.dev/packages/freezed&quot;&gt;Freezed&lt;/a&gt;, we can easily generate data classes for our application&apos;s state, which helps to reduce boilerplate code and ensures that our data model remains in sync with our state. The generated data classes also allow us to easily add new properties and update existing ones, ensuring that our data model remains up-to-date.&lt;/p&gt;
&lt;p&gt;First, we need to create a BLoC that will handle the logic for retrieving and updating the data. To do this, we&apos;ll create a new class &lt;code&gt;WordBloc&lt;/code&gt; that extends Bloc:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import &amp;#39;package:flutter/material.dart&amp;#39;;
import &amp;#39;package:injectable/injectable.dart&amp;#39;;
import &amp;#39;package:freezed_annotation/freezed_annotation.dart&amp;#39;;
import &amp;#39;package:bloc/bloc.dart&amp;#39;;
import &amp;#39;word_repository.dart&amp;#39;;

part &amp;#39;word_event.dart&amp;#39;;
part &amp;#39;word_state.dart&amp;#39;;

@injectable
class WordBloc extends Bloc&amp;lt;WordEvent, WordState&amp;gt; {
  final WordRepository _wordRepository;

  WordBloc(this._wordRepository) : super(WordState.initial());

  @override
  Stream&amp;lt;WordState&amp;gt; mapEventToState(WordEvent event) async* {
    if (event is FetchWords) {
      yield WordState.loading();
      try {
        final words = await _wordRepository.getWords();
        yield WordState.loaded(words: words);
      } catch (e) {
        yield WordState.error(errorMessage: e.toString());
      }
    }
  }
}

@freezed
abstract class WordEvent with _$WordEvent {
  const factory WordEvent.fetchWords() = FetchWords;
}

@freezed
abstract class WordState with _$WordState {
  const factory WordState.initial() = WordInitial;
  const factory WordState.loading() = WordLoading;
  const factory WordState.loaded({List&amp;lt;String&amp;gt; words}) = WordLoaded;
  const factory WordState.error({String errorMessage}) = WordError;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Injectable&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://pub.dev/packages/injectable&quot;&gt;Injectable&lt;/a&gt; provides a simple and flexible way to manage dependencies in our Flutter application. With Injectable, we can easily manage the lifecycle of our dependencies and provide the required instances to the parts of our application that need them. This can greatly simplify the process of writing tests and making changes to our application.&lt;/p&gt;
&lt;h3&gt;Chopper&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://pub.dev/packages/chopper&quot;&gt;Chopper&lt;/a&gt; library simplifies the process of making API calls and allows us to easily add and manage our API endpoints. This can be particularly useful when working on a large-scale application, where the number of API endpoints can become difficult to manage. Chopper also provides support for request and response interception, making it easy to add global error handling and logging.&lt;/p&gt;
&lt;p&gt;Let&apos;s rewrite the data layer of this app using Chopper for networking and Freezed for data models.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import &amp;#39;package:chopper/chopper.dart&amp;#39;;
import &amp;#39;package:clutterfree_flutter/data/models/word.dart&amp;#39;;
import &amp;#39;package:injectable/injectable.dart&amp;#39;;

part &amp;#39;word_repository.chopper.dart&amp;#39;;

@ChopperApi(baseUrl: &amp;#39;https://random-word-api.herokuapp.com&amp;#39;)
abstract class WordRepository extends ChopperService {
  @Get(path: &amp;#39;word?number=10&amp;#39;)
  Future&amp;lt;Response&amp;lt;List&amp;lt;Word&amp;gt;&amp;gt;&amp;gt; getWords();

  static WordRepository create() {
    final client = ChopperClient(
      baseUrl: &amp;#39;https://random-word-api.herokuapp.com&amp;#39;,
      services: [
        _$WordRepository(),
      ],
      converter: JsonConverter(),
    );
    return _$WordRepository(client);
  }
}

@freezed
abstract class Word with _$Word {
  const factory Word({String word}) = _Word;

  factory Word.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) =&amp;gt; _$WordFromJson(json);
}

@injectable
class ProductionWordRepository extends WordRepository {
  final ChopperClient chopperClient;

  ProductionWordRepository(this.chopperClient) : super(chopperClient);
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Auto Route&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://pub.dev/packages/auto_route&quot;&gt;Auto Route&lt;/a&gt; makes it easy to navigate between pages in our application and reduces the amount of boilerplate code required to manage navigation. With AutoRoute, we can define routes in a declarative way, reducing the risk of bugs and making it easier to maintain our application.&lt;/p&gt;
&lt;p&gt;You can use the &lt;code&gt;generateRoute&lt;/code&gt; function from the &lt;code&gt;AutoRouter&lt;/code&gt; class to define the routes using the AutoRoute library. Here&apos;s an example of how you can define the routes for your app:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import &amp;#39;package:auto_route/auto_route.dart&amp;#39;;
import &amp;#39;package:flutter/material.dart&amp;#39;;
import &amp;#39;package:injectable/injectable.dart&amp;#39;;
import &amp;#39;package:clutterfree_flutter/ui/views/home_page.dart&amp;#39;;
import &amp;#39;package:clutterfree_flutter/ui/views/detail_page.dart&amp;#39;;

@Injectable(as: Router)
class $Router extends RouterBase {
  @override
  Widget generateRoute(RouteSettings settings) {
    switch (settings.name) {
      case Routes.homePage:
        return MaterialPageRoute&amp;lt;dynamic&amp;gt;(
          builder: (_) =&amp;gt; HomePage(),
        );
      case Routes.detailPage:
        var word = settings.arguments as String;
        return MaterialPageRoute&amp;lt;dynamic&amp;gt;(
          builder: (_) =&amp;gt; DetailPage(word: word),
        );
      default:
        return MaterialPageRoute&amp;lt;dynamic&amp;gt;(
          builder: (_) =&amp;gt; Scaffold(
            body: Center(
              child: Text(&amp;#39;No route defined for ${settings.name}&amp;#39;),
            ),
          ),
        );
    }
  }
}

class Routes {
  static const String homePage = &amp;#39;/&amp;#39;;
  static const String detailPage = &amp;#39;/detail&amp;#39;;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Screen Util&lt;/h3&gt;
&lt;p&gt;Finally, the &lt;a href=&quot;https://pub.dev/packages/flutter_screenutil&quot;&gt;Screen Util&lt;/a&gt; library makes it easy to build responsive user interfaces in Flutter. With ScreenUtil, we can easily adjust the size and position of our UI elements based on the size of the screen, ensuring that our application looks great on all devices.&lt;/p&gt;
&lt;p&gt;Let&apos;s rewrite the UI and bind all the components together. We will be using Screen Util for scaling and AutoRoute for routing.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import &amp;#39;package:flutter/material.dart&amp;#39;;
import &amp;#39;package:flutter_bloc/flutter_bloc.dart&amp;#39;;
import &amp;#39;package:screenutils/screenutils.dart&amp;#39;;
import &amp;#39;package:clutterfree_flutter/word_bloc.dart&amp;#39;;
import &amp;#39;package:clutterfree_flutter/models/word.dart&amp;#39;;
import &amp;#39;package:clutterfree_flutter/word_repository.dart&amp;#39;;

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider&amp;lt;WordBloc&amp;gt;(
      create: (context) =&amp;gt; WordBloc(WordRepository()),
      child: Scaffold(
        appBar: AppBar(
          title: Text(&amp;#39;Clutterfree Flutter&amp;#39;),
        ),
        body: BlocBuilder&amp;lt;WordBloc, WordState&amp;gt;(
          builder: (context, state) {
            return state.when(
              initial: () =&amp;gt; Center(
                child: CircularProgressIndicator(),
              ),
              loading: () =&amp;gt; Center(
                child: CircularProgressIndicator(),
              ),
              loaded: (words) {
                return ListView.builder(
                  itemCount: words.length,
                  itemBuilder: (context, index) {
                    return ListTile(
                      title: Text(words[index].word, style: TextStyle(fontSize: 20.sp)),
                      onTap: () {
                        Navigator.pushNamed(context, &amp;#39;word_detail&amp;#39;, arguments: words[index].word);
                      },
                    );
                  },
                );
              },
              error: () =&amp;gt; Center(
                child: Text(&amp;#39;Error Occurred&amp;#39;, style: TextStyle(fontSize: 20.sp)),
              ),
            );
          },
        ),
      ),
    );
  }
}

class DetailPage extends StatelessWidget {
  final String word;

  DetailPage({this.word});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(word),
      ),
      body: Center(
        child: Text(word, style: TextStyle(fontSize: 30.sp)),
      ),
    );
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;hr&gt;
&lt;p&gt;In conclusion, by using these libraries, we can build clean, scalable and maintainable Flutter applications that are easier to develop, test, and maintain. By using a combination of Bloc, Freezed, Chopper, AutoRoute, Injectable and ScreenUtil, we can take advantage of the latest and greatest in Flutter development and ensure that our applications are ready for the future.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Automatic JSON to Dart Conversion in Flutter]]></title><description><![CDATA[Flutter is a popular mobile app development framework that allows developers to build beautiful and high-performance apps for iOS and…]]></description><link>https://www.zuhaibahmad.com/flutter-json-to-dart/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/flutter-json-to-dart/</guid><pubDate>Wed, 13 May 2020 12:33:00 GMT</pubDate><content:encoded>&lt;p&gt;Flutter is a popular mobile app development framework that allows developers to build beautiful and high-performance apps for iOS and Android from a single codebase. One of the common tasks when building apps with Flutter is working with JSON data.&lt;/p&gt;
&lt;p&gt;JSON (JavaScript Object Notation) is a lightweight data interchange format that is easy for humans to read and write and easy for machines to parse and generate. It’s commonly used for transmitting data between a server and a web application.&lt;/p&gt;
&lt;p&gt;In Flutter, you can use the &lt;code&gt;dart:convert&lt;/code&gt; library to convert JSON data into Dart objects. However, manually writing the code to convert JSON data into Dart objects can be tedious and error-prone.&lt;/p&gt;
&lt;p&gt;Fortunately, there are several tools available that can automatically generate the Dart code for you. These tools take a JSON object as input and generate the corresponding Dart classes with all the necessary methods for encoding and decoding the JSON data.&lt;/p&gt;
&lt;p&gt;One such tool is &lt;a href=&quot;https://pub.dev/packages/json_serializable&quot;&gt;json_serializable&lt;/a&gt;, which generates code using source_gen and build_runner. To use &lt;a href=&quot;https://pub.dev/packages/json_serializable&quot;&gt;json_serializable&lt;/a&gt; in your Flutter project, you need to add it as a dependency in your &lt;code&gt;pubspec.yaml&lt;/code&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;yaml&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;dependencies:
  json_annotation: ^4.0.1

dev_dependencies:
  build_runner: ^2.0.6
  json_serializable: ^4.1.3&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Then run &lt;code&gt;flutter pub get&lt;/code&gt; to install the dependencies.&lt;/p&gt;
&lt;p&gt;Next, you need to create a model class for your JSON data and annotate it with &lt;code&gt;@JsonSerializable()&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;dart&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import &amp;#39;package:json_annotation/json_annotation.dart&amp;#39;;

part &amp;#39;user.g.dart&amp;#39;;

@JsonSerializable()
class User {
  final String name;
  final String email;

  User(this.name, this.email);

  factory User.fromJson(Map&amp;lt;String, dynamic&amp;gt; json) =&amp;gt; _$UserFromJson(json);
  Map&amp;lt;String, dynamic&amp;gt; toJson() =&amp;gt;_$UserToJson(this);
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Finally, run &lt;code&gt;flutter pub run build_runner build&lt;/code&gt; to generate the necessary code.&lt;/p&gt;
&lt;p&gt;Another option is &lt;a href=&quot;https://javiercbk.github.io/json_to_dart/&quot;&gt;json_to_dart&lt;/a&gt;, an online tool that generates Dart classes from JSON input. To use &lt;a href=&quot;https://javiercbk.github.io/json_to_dart/&quot;&gt;json_to_dart&lt;/a&gt;, simply paste your JSON data into the input field on their website and click “Generate”. The tool will then generate the corresponding Dart classes for you.&lt;/p&gt;
&lt;p&gt;Using these tools can save you time and reduce errors when working with JSON data in your Flutter app.&lt;/p&gt;
&lt;p&gt;In conclusion, automatic JSON to Dart conversion in Flutter can make your development process faster and more efficient. By using tools like &lt;a href=&quot;https://pub.dev/packages/json_serializable&quot;&gt;json_serializable&lt;/a&gt; or &lt;a href=&quot;https://javiercbk.github.io/json_to_dart/&quot;&gt;json_to_dart&lt;/a&gt;, you can easily convert your JSON data into usable Dart objects without having to write any additional code. This saves time and reduces errors, allowing you to focus on building great apps with Flutter.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Painless UI Testing with Kaspresso]]></title><description><![CDATA[A few months ago I wrote a blog post under a similar title{:target="_blank"} about better Unit Testing for Kotlin with Kotest{:target…]]></description><link>https://www.zuhaibahmad.com/android-ui-spec-testing/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/android-ui-spec-testing/</guid><pubDate>Sat, 14 Mar 2020 11:00:00 GMT</pubDate><content:encoded>&lt;p&gt;A few months ago I wrote a blog post under a &lt;a href=&quot;/android-spec-testing/&quot;&gt;similar title&lt;/a&gt;{:target=&quot;_blank&quot;} about better Unit Testing for Kotlin with &lt;a href=&quot;https://github.com/kotest/kotest&quot;&gt;Kotest&lt;/a&gt;{:target=&quot;_blank&quot;} (formerly KotlinTest). The focus of that post was to demonstrate a more organized, boilerplate free and self-documenting TDD/BDD friendly testing framework for Kotlin.&lt;/p&gt;
&lt;p&gt;I have been using Kotest for about a year now and it is safe to say that it has encouraged me to test more of my code. While frameworks like Kotest and Spek provide a good alternative for unit testing, the UI part still feels lacking. Most of us still use Espresso for writing UI tests that, even with its super clean API, feel very repetitive and verbose. For some time, I worked around this by extracting the repetitive code for reuse. It wasn&apos;t very clean.&lt;/p&gt;
&lt;h3&gt;Caffeine Overflow&lt;/h3&gt;
&lt;p&gt;Part of my dev-wishlist was to someday establish this perfect loop of TDD flow with Android where all the features start with a failing UI test, followed by multiple unit tests to satisfy the business logic. At the end of which we have a solid well-tested and self-documented feature. So I started searching for other options which lead me to several frameworks, most of which were named after a coffee.&lt;/p&gt;
&lt;p&gt;One of the great finds was &lt;a href=&quot;https://github.com/agoda-com/Kakao&quot;&gt;Kakao&lt;/a&gt;{:target=&quot;_blank&quot;} by Agoda, it provides a more readable and reusable DSL on top of Espresso. The idea is to encapsulate the UI in &lt;code&gt;Screen&lt;/code&gt; interfaces which can then be reused. Here is an example of what a form UI would look like:&lt;/p&gt;
&lt;p&gt;&lt;div id=&quot;gist101813537&quot; class=&quot;gist&quot;&gt;
    &lt;div class=&quot;gist-file&quot; translate=&quot;no&quot; data-color-mode=&quot;light&quot; data-light-theme=&quot;light&quot;&gt;
      &lt;div class=&quot;gist-data&quot;&gt;
        
&lt;div class=&quot;js-gist-file-update-container js-task-list-container&quot;&gt;
      &lt;div id=&quot;file-formscreen-kt&quot; class=&quot;file my-2&quot;&gt;
    
    &lt;div itemprop=&quot;text&quot;
      class=&quot;Box-body p-0 blob-wrapper data type-kotlin  &quot;
      style=&quot;overflow: auto&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;FormScreen.kt content, created by xuhaibahmad on 05:11AM on March 15, 2020.&quot;
    &gt;

        
&lt;div class=&quot;js-check-hidden-unicode js-blob-code-container blob-code-content&quot;&gt;

  &lt;template class=&quot;js-file-alert-template&quot;&gt;
  &lt;div data-view-component=&quot;true&quot; class=&quot;flash flash-warn flash-full d-flex flex-items-center&quot;&gt;
  &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
    &lt;span&gt;
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.co/hiddenchars&quot; target=&quot;_blank&quot;&gt;Learn more about bidirectional Unicode characters&lt;/a&gt;
    &lt;/span&gt;


  &lt;div data-view-component=&quot;true&quot; class=&quot;flash-action&quot;&gt;        &lt;a href=&quot;{{ revealButtonHref }}&quot; data-view-component=&quot;true&quot; class=&quot;btn-sm btn&quot;&gt;    Show hidden characters
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/template&gt;
&lt;template class=&quot;js-line-alert-template&quot;&gt;
  &lt;span aria-label=&quot;This line has hidden Unicode characters&quot; data-view-component=&quot;true&quot; class=&quot;line-alert tooltipped tooltipped-e&quot;&gt;
    &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/span&gt;&lt;/template&gt;

  &lt;table data-hpc class=&quot;highlight tab-size js-file-line-container&quot; data-tab-size=&quot;4&quot; data-paste-markdown-skip data-tagsearch-path=&quot;FormScreen.kt&quot;&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formscreen-kt-L1&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;1&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formscreen-kt-LC1&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;class FormScreen : Screen&amp;lt;FormScreen&amp;gt;() {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formscreen-kt-L2&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;2&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formscreen-kt-LC2&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    val phone = KView { withId(R.id.phone) }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formscreen-kt-L3&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;3&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formscreen-kt-LC3&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    val email = KEditText { withId(R.id.email) }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formscreen-kt-L4&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;4&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formscreen-kt-LC4&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    val submit = KButton { withId(R.id.submit) }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formscreen-kt-L5&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;5&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formscreen-kt-LC5&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;}&lt;/td&gt;
        &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;


    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;

      &lt;/div&gt;
      &lt;div class=&quot;gist-meta&quot;&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0/raw/dfe4006ce0cc8518371be19b4ceb35d811a082db/FormScreen.kt&quot; style=&quot;float:right&quot; class=&quot;Link--inTextBlock&quot;&gt;view raw&lt;/a&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0#file-formscreen-kt&quot; class=&quot;Link--inTextBlock&quot;&gt;
          FormScreen.kt
        &lt;/a&gt;
        hosted with &amp;#10084; by &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;The resulting tests are reduced to a combination of actions and assertions inside the screen:&lt;/p&gt;
&lt;p&gt;&lt;div id=&quot;gist101813537&quot; class=&quot;gist&quot;&gt;
    &lt;div class=&quot;gist-file&quot; translate=&quot;no&quot; data-color-mode=&quot;light&quot; data-light-theme=&quot;light&quot;&gt;
      &lt;div class=&quot;gist-data&quot;&gt;
        
&lt;div class=&quot;js-gist-file-update-container js-task-list-container&quot;&gt;
      &lt;div id=&quot;file-formtest-kt&quot; class=&quot;file my-2&quot;&gt;
    
    &lt;div itemprop=&quot;text&quot;
      class=&quot;Box-body p-0 blob-wrapper data type-kotlin  &quot;
      style=&quot;overflow: auto&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;FormTest.kt content, created by xuhaibahmad on 05:11AM on March 15, 2020.&quot;
    &gt;

        
&lt;div class=&quot;js-check-hidden-unicode js-blob-code-container blob-code-content&quot;&gt;

  &lt;template class=&quot;js-file-alert-template&quot;&gt;
  &lt;div data-view-component=&quot;true&quot; class=&quot;flash flash-warn flash-full d-flex flex-items-center&quot;&gt;
  &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
    &lt;span&gt;
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.co/hiddenchars&quot; target=&quot;_blank&quot;&gt;Learn more about bidirectional Unicode characters&lt;/a&gt;
    &lt;/span&gt;


  &lt;div data-view-component=&quot;true&quot; class=&quot;flash-action&quot;&gt;        &lt;a href=&quot;{{ revealButtonHref }}&quot; data-view-component=&quot;true&quot; class=&quot;btn-sm btn&quot;&gt;    Show hidden characters
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/template&gt;
&lt;template class=&quot;js-line-alert-template&quot;&gt;
  &lt;span aria-label=&quot;This line has hidden Unicode characters&quot; data-view-component=&quot;true&quot; class=&quot;line-alert tooltipped tooltipped-e&quot;&gt;
    &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/span&gt;&lt;/template&gt;

  &lt;table data-hpc class=&quot;highlight tab-size js-file-line-container&quot; data-tab-size=&quot;4&quot; data-paste-markdown-skip data-tagsearch-path=&quot;FormTest.kt&quot;&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formtest-kt-L1&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;1&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formtest-kt-LC1&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;onScreen&amp;lt;FormScreen&amp;gt; {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formtest-kt-L2&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;2&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formtest-kt-LC2&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    phone {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formtest-kt-L3&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;3&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formtest-kt-LC3&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;       hasText(&amp;quot;971201771&amp;quot;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formtest-kt-L4&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;4&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formtest-kt-LC4&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formtest-kt-L5&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;5&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formtest-kt-LC5&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    button {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formtest-kt-L6&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;6&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formtest-kt-LC6&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;       click()&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formtest-kt-L7&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;7&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formtest-kt-LC7&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-formtest-kt-L8&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;8&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-formtest-kt-LC8&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;}&lt;/td&gt;
        &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;


    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;

      &lt;/div&gt;
      &lt;div class=&quot;gist-meta&quot;&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0/raw/dfe4006ce0cc8518371be19b4ceb35d811a082db/FormTest.kt&quot; style=&quot;float:right&quot; class=&quot;Link--inTextBlock&quot;&gt;view raw&lt;/a&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0#file-formtest-kt&quot; class=&quot;Link--inTextBlock&quot;&gt;
          FormTest.kt
        &lt;/a&gt;
        hosted with &amp;#10084; by &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;h3&gt;But Wait, There&apos;s More!&lt;/h3&gt;
&lt;p&gt;Kakao seemed like a pretty reasonable solution until I stumbled upon &lt;a href=&quot;https://github.com/KasperskyLab/Kaspresso&quot;&gt;Kaspresso&lt;/a&gt;{:target=&quot;_blank&quot;}. It combines Espresso and UI Automator into single DSL based API. For the Espresso part, it just wraps around Kakao with some added features such as improved logging, built-in screenshots, ADB command execution, runtime permissions management and more.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Grade Calculator 2.0&lt;/h3&gt;
&lt;p&gt;What could be a better example than to revisit the grade calculator we built in the previous post? Let&apos;s add some UI and this time build in a purely Test-Driven style.&lt;/p&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;Let&apos;s create a simple Android project with an empty Activity. Then add the following test dependencies:&lt;/p&gt;
&lt;p&gt;&lt;div id=&quot;gist101813537&quot; class=&quot;gist&quot;&gt;
    &lt;div class=&quot;gist-file&quot; translate=&quot;no&quot; data-color-mode=&quot;light&quot; data-light-theme=&quot;light&quot;&gt;
      &lt;div class=&quot;gist-data&quot;&gt;
        
&lt;div class=&quot;js-gist-file-update-container js-task-list-container&quot;&gt;
      &lt;div id=&quot;file-testdeps-gradle&quot; class=&quot;file my-2&quot;&gt;
    
    &lt;div itemprop=&quot;text&quot;
      class=&quot;Box-body p-0 blob-wrapper data type-gradle  &quot;
      style=&quot;overflow: auto&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;TestDeps.gradle content, created by xuhaibahmad on 05:11AM on March 15, 2020.&quot;
    &gt;

        
&lt;div class=&quot;js-check-hidden-unicode js-blob-code-container blob-code-content&quot;&gt;

  &lt;template class=&quot;js-file-alert-template&quot;&gt;
  &lt;div data-view-component=&quot;true&quot; class=&quot;flash flash-warn flash-full d-flex flex-items-center&quot;&gt;
  &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
    &lt;span&gt;
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.co/hiddenchars&quot; target=&quot;_blank&quot;&gt;Learn more about bidirectional Unicode characters&lt;/a&gt;
    &lt;/span&gt;


  &lt;div data-view-component=&quot;true&quot; class=&quot;flash-action&quot;&gt;        &lt;a href=&quot;{{ revealButtonHref }}&quot; data-view-component=&quot;true&quot; class=&quot;btn-sm btn&quot;&gt;    Show hidden characters
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/template&gt;
&lt;template class=&quot;js-line-alert-template&quot;&gt;
  &lt;span aria-label=&quot;This line has hidden Unicode characters&quot; data-view-component=&quot;true&quot; class=&quot;line-alert tooltipped tooltipped-e&quot;&gt;
    &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/span&gt;&lt;/template&gt;

  &lt;table data-hpc class=&quot;highlight tab-size js-file-line-container&quot; data-tab-size=&quot;4&quot; data-paste-markdown-skip data-tagsearch-path=&quot;TestDeps.gradle&quot;&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testdeps-gradle-L1&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;1&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testdeps-gradle-LC1&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;testImplementation &lt;span class=&quot;pl-s&quot;&gt;&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;junit:junit:4.13&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testdeps-gradle-L2&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;2&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testdeps-gradle-LC2&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;implementation &lt;span class=&quot;pl-s&quot;&gt;&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;androidx.test:core:1.2.0&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testdeps-gradle-L3&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;3&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testdeps-gradle-LC3&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;androidTestImplementation &lt;span class=&quot;pl-s&quot;&gt;&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;androidx.test.ext:junit:1.1.1&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testdeps-gradle-L4&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;4&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testdeps-gradle-LC4&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;testImplementation &lt;span class=&quot;pl-s&quot;&gt;&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;io.kotlintest:kotlintest-runner-junit5:3.1.7&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testdeps-gradle-L5&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;5&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testdeps-gradle-LC5&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;testImplementation &lt;span class=&quot;pl-s&quot;&gt;&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;io.mockk:mockk:1.9.3&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testdeps-gradle-L6&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;6&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testdeps-gradle-LC6&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;androidTestImplementation &lt;span class=&quot;pl-s&quot;&gt;&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;com.kaspersky.android-components:kaspresso:1.1.0&lt;span class=&quot;pl-pds&quot;&gt;&amp;#39;&lt;/span&gt;&lt;/span&gt;&lt;/td&gt;
        &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;


    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;

      &lt;/div&gt;
      &lt;div class=&quot;gist-meta&quot;&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0/raw/dfe4006ce0cc8518371be19b4ceb35d811a082db/TestDeps.gradle&quot; style=&quot;float:right&quot; class=&quot;Link--inTextBlock&quot;&gt;view raw&lt;/a&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0#file-testdeps-gradle&quot; class=&quot;Link--inTextBlock&quot;&gt;
          TestDeps.gradle
        &lt;/a&gt;
        hosted with &amp;#10084; by &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;If you set up your project using the Android Studio wizard, then most of these dependencies should already be present. The only dependencies that we must add manually are Kotest, Mockk, and Kaspresso.&lt;/p&gt;
&lt;p&gt;Kotest and Mockk are the same as last time for unit testing and mocking, while Kaspresso will be used for UI tests.&lt;/p&gt;
&lt;h3&gt;Adding The UI Test&lt;/h3&gt;
&lt;p&gt;Let&apos;s add a UI test for the happy path of our calculator without touching the activity first:&lt;/p&gt;
&lt;p&gt;&lt;div id=&quot;gist101813537&quot; class=&quot;gist&quot;&gt;
    &lt;div class=&quot;gist-file&quot; translate=&quot;no&quot; data-color-mode=&quot;light&quot; data-light-theme=&quot;light&quot;&gt;
      &lt;div class=&quot;gist-data&quot;&gt;
        
&lt;div class=&quot;js-gist-file-update-container js-task-list-container&quot;&gt;
      &lt;div id=&quot;file-gradecalculatoruiteststep1-kt&quot; class=&quot;file my-2&quot;&gt;
    
    &lt;div itemprop=&quot;text&quot;
      class=&quot;Box-body p-0 blob-wrapper data type-kotlin  &quot;
      style=&quot;overflow: auto&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;GradeCalculatorUiTestStep1.kt content, created by xuhaibahmad on 05:11AM on March 15, 2020.&quot;
    &gt;

        
&lt;div class=&quot;js-check-hidden-unicode js-blob-code-container blob-code-content&quot;&gt;

  &lt;template class=&quot;js-file-alert-template&quot;&gt;
  &lt;div data-view-component=&quot;true&quot; class=&quot;flash flash-warn flash-full d-flex flex-items-center&quot;&gt;
  &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
    &lt;span&gt;
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.co/hiddenchars&quot; target=&quot;_blank&quot;&gt;Learn more about bidirectional Unicode characters&lt;/a&gt;
    &lt;/span&gt;


  &lt;div data-view-component=&quot;true&quot; class=&quot;flash-action&quot;&gt;        &lt;a href=&quot;{{ revealButtonHref }}&quot; data-view-component=&quot;true&quot; class=&quot;btn-sm btn&quot;&gt;    Show hidden characters
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/template&gt;
&lt;template class=&quot;js-line-alert-template&quot;&gt;
  &lt;span aria-label=&quot;This line has hidden Unicode characters&quot; data-view-component=&quot;true&quot; class=&quot;line-alert tooltipped tooltipped-e&quot;&gt;
    &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/span&gt;&lt;/template&gt;

  &lt;table data-hpc class=&quot;highlight tab-size js-file-line-container&quot; data-tab-size=&quot;4&quot; data-paste-markdown-skip data-tagsearch-path=&quot;GradeCalculatorUiTestStep1.kt&quot;&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L1&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;1&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC1&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;import androidx.test.rule.ActivityTestRule&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L2&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;2&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC2&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;import com.kaspersky.kaspresso.testcases.api.testcase.TestCase&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L3&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;3&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC3&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;import com.zuhaibahmad.kaspressotestingdemo.screens.GradeCalculatorScreen&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L4&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;4&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC4&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;import org.junit.Rule&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L5&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;5&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC5&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;import org.junit.Test&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L6&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;6&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC6&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L7&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;7&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC7&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;class GradeCalculatorUiTest : TestCase() {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L8&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;8&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC8&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L9&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;9&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC9&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    @Rule&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L10&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;10&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC10&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    @JvmField&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L11&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;11&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC11&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    var rule = ActivityTestRule(GradeCalculatorActivity::class.java, false, false)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L12&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;12&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC12&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L13&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;13&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC13&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    val screen = GradeCalculatorScreen()&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L14&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;14&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC14&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L15&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;15&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC15&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    @Test&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L16&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;16&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC16&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    fun onCorrectScoreSubmit_shouldDisplayCorrectGrade() = before {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L17&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;17&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC17&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        // No-op&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L18&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;18&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC18&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    }.after {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L19&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;19&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC19&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        // No-op&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L20&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;20&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC20&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    }.run {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L21&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;21&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC21&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        screen {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L22&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;22&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC22&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            step(&amp;quot;Open grade calculator screen&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L23&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;23&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC23&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                rule.launchActivity(null)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L24&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;24&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC24&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L25&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;25&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC25&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L26&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;26&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC26&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            step(&amp;quot;Submit obtained marks&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L27&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;27&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC27&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                inputMarks.replaceText(&amp;quot;90&amp;quot;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L28&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;28&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC28&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                buttonSubmit.click()&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L29&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;29&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC29&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L30&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;30&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC30&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L31&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;31&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC31&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            step(&amp;quot;Verify the displayed grade is correct&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L32&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;32&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC32&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                labelGrade.hasText(&amp;quot;Your Grade is: A&amp;quot;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L33&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;33&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC33&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L34&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;34&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC34&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L35&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;35&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC35&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-L36&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;36&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoruiteststep1-kt-LC36&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;}&lt;/td&gt;
        &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;


    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;

      &lt;/div&gt;
      &lt;div class=&quot;gist-meta&quot;&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0/raw/dfe4006ce0cc8518371be19b4ceb35d811a082db/GradeCalculatorUiTestStep1.kt&quot; style=&quot;float:right&quot; class=&quot;Link--inTextBlock&quot;&gt;view raw&lt;/a&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0#file-gradecalculatoruiteststep1-kt&quot; class=&quot;Link--inTextBlock&quot;&gt;
          GradeCalculatorUiTestStep1.kt
        &lt;/a&gt;
        hosted with &amp;#10084; by &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;If we try to run this test, it will fail because we have not added the &lt;code&gt;GradeCalculatorScreen&lt;/code&gt; yet. This test serves as a blueprint of what we want to achieve for the happy path of grade calculation. Let&apos;s break down each step:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;TestCase&lt;/strong&gt; - The first thing you might have noticed is that the test class extends &lt;code&gt;TestCase&lt;/code&gt;, which is a base class provided by Kaspresso that gives access to most of its features.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Activity Test Rule&lt;/strong&gt; - Next, we create an activity test rule for the grade activity. This part is the same as normal Instrumentation tests.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GradeCalculatorScreen Instance&lt;/strong&gt; - This instance of the screen will be used in the Kaspresso DSL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Test Body&lt;/strong&gt; - For the test body, you might notice the chain of 3 separate blocks. You can use the block syntax instead of the expression body, but then it will add an extra indent to all the code inside the block which I do not like very much.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Before-After-Run&lt;/strong&gt; - The &lt;code&gt;before&lt;/code&gt; and &lt;code&gt;after&lt;/code&gt; blocks are interceptors for anything you might want to do before or after the test respectively. For example, &lt;code&gt;before&lt;/code&gt; can be used for mocking and &lt;code&gt;after&lt;/code&gt; can be used to release resources.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Steps&lt;/strong&gt; - Finally, the run block is where actual testing happens
&lt;ul&gt;
&lt;li&gt;Every step is described with a &lt;code&gt;step&lt;/code&gt; block. This part is optional, you can skip the steps if you want and directly write the encapsulating code. However, it is very helpful since not only it provides a comment on what is happening but this information also gets printed in the logs.&lt;/li&gt;
&lt;li&gt;In the first step, we launch the activity, this can be done directly with the test rule though.&lt;/li&gt;
&lt;li&gt;In the next two steps, we perform some actions on the &lt;code&gt;GradeCalculatorScreen&lt;/code&gt; and then verify their results. Note that all the methods come from the library, we only provide view bindings in the &lt;code&gt;Screen&lt;/code&gt; implementations.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Implementing The Kaspresso Screen&lt;/h3&gt;
&lt;p&gt;At this point, we cannot even compile the application due to missing classes. Let&apos;s provide an implementation for our &lt;code&gt;GradeCalculatorScreen&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;div id=&quot;gist101813537&quot; class=&quot;gist&quot;&gt;
    &lt;div class=&quot;gist-file&quot; translate=&quot;no&quot; data-color-mode=&quot;light&quot; data-light-theme=&quot;light&quot;&gt;
      &lt;div class=&quot;gist-data&quot;&gt;
        
&lt;div class=&quot;js-gist-file-update-container js-task-list-container&quot;&gt;
      &lt;div id=&quot;file-gradecalculatorscreen-kt&quot; class=&quot;file my-2&quot;&gt;
    
    &lt;div itemprop=&quot;text&quot;
      class=&quot;Box-body p-0 blob-wrapper data type-kotlin  &quot;
      style=&quot;overflow: auto&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;GradeCalculatorScreen.kt content, created by xuhaibahmad on 05:11AM on March 15, 2020.&quot;
    &gt;

        
&lt;div class=&quot;js-check-hidden-unicode js-blob-code-container blob-code-content&quot;&gt;

  &lt;template class=&quot;js-file-alert-template&quot;&gt;
  &lt;div data-view-component=&quot;true&quot; class=&quot;flash flash-warn flash-full d-flex flex-items-center&quot;&gt;
  &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
    &lt;span&gt;
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.co/hiddenchars&quot; target=&quot;_blank&quot;&gt;Learn more about bidirectional Unicode characters&lt;/a&gt;
    &lt;/span&gt;


  &lt;div data-view-component=&quot;true&quot; class=&quot;flash-action&quot;&gt;        &lt;a href=&quot;{{ revealButtonHref }}&quot; data-view-component=&quot;true&quot; class=&quot;btn-sm btn&quot;&gt;    Show hidden characters
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/template&gt;
&lt;template class=&quot;js-line-alert-template&quot;&gt;
  &lt;span aria-label=&quot;This line has hidden Unicode characters&quot; data-view-component=&quot;true&quot; class=&quot;line-alert tooltipped tooltipped-e&quot;&gt;
    &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/span&gt;&lt;/template&gt;

  &lt;table data-hpc class=&quot;highlight tab-size js-file-line-container&quot; data-tab-size=&quot;4&quot; data-paste-markdown-skip data-tagsearch-path=&quot;GradeCalculatorScreen.kt&quot;&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L1&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;1&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC1&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;import com.agoda.kakao.edit.KEditText&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L2&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;2&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC2&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;import com.agoda.kakao.screen.Screen&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L3&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;3&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC3&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;import com.agoda.kakao.text.KButton&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L4&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;4&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC4&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;import com.agoda.kakao.text.KTextView&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L5&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;5&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC5&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;import com.zuhaibahmad.kaspressotestingdemo.R&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L6&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;6&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC6&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L7&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;7&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC7&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;class GradeCalculatorScreen: Screen&amp;lt;GradeCalculatorScreen&amp;gt;() {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L8&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;8&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC8&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    val inputMarks = KEditText { withId(R.id.etMarks) }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L9&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;9&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC9&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    val buttonSubmit = KButton { withId(R.id.btSubmit) }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L10&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;10&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC10&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    val labelGrade = KTextView { withId(R.id.tvGrade) }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-L11&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;11&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorscreen-kt-LC11&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;}&lt;/td&gt;
        &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;


    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;

      &lt;/div&gt;
      &lt;div class=&quot;gist-meta&quot;&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0/raw/dfe4006ce0cc8518371be19b4ceb35d811a082db/GradeCalculatorScreen.kt&quot; style=&quot;float:right&quot; class=&quot;Link--inTextBlock&quot;&gt;view raw&lt;/a&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0#file-gradecalculatorscreen-kt&quot; class=&quot;Link--inTextBlock&quot;&gt;
          GradeCalculatorScreen.kt
        &lt;/a&gt;
        hosted with &amp;#10084; by &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;As explained earlier, the screen class just binds the UI elements and organizes them into one place. Since we do not have these elements in the layout XML, the project won&apos;t compile unless we add them too. So here&apos;s a simple layout for it:&lt;/p&gt;
&lt;p&gt;&lt;div id=&quot;gist101813537&quot; class=&quot;gist&quot;&gt;
    &lt;div class=&quot;gist-file&quot; translate=&quot;no&quot; data-color-mode=&quot;light&quot; data-light-theme=&quot;light&quot;&gt;
      &lt;div class=&quot;gist-data&quot;&gt;
        
&lt;div class=&quot;js-gist-file-update-container js-task-list-container&quot;&gt;
      &lt;div id=&quot;file-activity_grade_calculator-xml&quot; class=&quot;file my-2&quot;&gt;
    
    &lt;div itemprop=&quot;text&quot;
      class=&quot;Box-body p-0 blob-wrapper data type-xml  &quot;
      style=&quot;overflow: auto&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;activity_grade_calculator.xml content, created by xuhaibahmad on 05:11AM on March 15, 2020.&quot;
    &gt;

        
&lt;div class=&quot;js-check-hidden-unicode js-blob-code-container blob-code-content&quot;&gt;

  &lt;template class=&quot;js-file-alert-template&quot;&gt;
  &lt;div data-view-component=&quot;true&quot; class=&quot;flash flash-warn flash-full d-flex flex-items-center&quot;&gt;
  &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
    &lt;span&gt;
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.co/hiddenchars&quot; target=&quot;_blank&quot;&gt;Learn more about bidirectional Unicode characters&lt;/a&gt;
    &lt;/span&gt;


  &lt;div data-view-component=&quot;true&quot; class=&quot;flash-action&quot;&gt;        &lt;a href=&quot;{{ revealButtonHref }}&quot; data-view-component=&quot;true&quot; class=&quot;btn-sm btn&quot;&gt;    Show hidden characters
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/template&gt;
&lt;template class=&quot;js-line-alert-template&quot;&gt;
  &lt;span aria-label=&quot;This line has hidden Unicode characters&quot; data-view-component=&quot;true&quot; class=&quot;line-alert tooltipped tooltipped-e&quot;&gt;
    &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/span&gt;&lt;/template&gt;

  &lt;table data-hpc class=&quot;highlight tab-size js-file-line-container&quot; data-tab-size=&quot;4&quot; data-paste-markdown-skip data-tagsearch-path=&quot;activity_grade_calculator.xml&quot;&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L1&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;1&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC1&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L2&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;2&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC2&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;&amp;lt;androidx.constraintlayout.widget.ConstraintLayout &lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L3&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;3&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC3&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L4&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;4&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC4&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    xmlns:app=&amp;quot;http://schemas.android.com/apk/res-auto&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L5&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;5&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC5&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    xmlns:tools=&amp;quot;http://schemas.android.com/tools&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L6&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;6&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC6&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    android:layout_width=&amp;quot;match_parent&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L7&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;7&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC7&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    android:layout_height=&amp;quot;match_parent&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L8&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;8&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC8&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    android:padding=&amp;quot;16dp&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L9&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;9&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC9&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    tools:context=&amp;quot;.GradeCalculatorActivity&amp;quot;&amp;gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L10&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;10&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC10&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L11&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;11&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC11&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    &amp;lt;androidx.appcompat.widget.AppCompatEditText&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L12&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;12&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC12&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:id=&amp;quot;@+id/etMarks&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L13&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;13&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC13&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:layout_width=&amp;quot;0dp&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L14&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;14&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC14&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:layout_height=&amp;quot;wrap_content&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L15&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;15&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC15&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:gravity=&amp;quot;center&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L16&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;16&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC16&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:hint=&amp;quot;@string/hint_enter_your_marks&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L17&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;17&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC17&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        app:layout_constraintBottom_toBottomOf=&amp;quot;parent&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L18&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;18&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC18&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        app:layout_constraintLeft_toLeftOf=&amp;quot;parent&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L19&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;19&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC19&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        app:layout_constraintRight_toRightOf=&amp;quot;parent&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L20&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;20&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC20&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        app:layout_constraintTop_toTopOf=&amp;quot;parent&amp;quot; /&amp;gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L21&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;21&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC21&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L22&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;22&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC22&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    &amp;lt;androidx.appcompat.widget.AppCompatTextView&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L23&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;23&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC23&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:id=&amp;quot;@+id/tvGrade&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L24&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;24&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC24&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:layout_width=&amp;quot;0dp&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L25&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;25&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC25&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:layout_height=&amp;quot;wrap_content&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L26&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;26&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC26&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:gravity=&amp;quot;center&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L27&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;27&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC27&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:textAppearance=&amp;quot;@style/TextAppearance.AppCompat.Large&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L28&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;28&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC28&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:textColor=&amp;quot;@color/colorAccent&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L29&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;29&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC29&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        app:layout_constraintEnd_toEndOf=&amp;quot;parent&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L30&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;30&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC30&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        app:layout_constraintStart_toStartOf=&amp;quot;parent&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L31&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;31&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC31&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        app:layout_constraintTop_toBottomOf=&amp;quot;@id/etMarks&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L32&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;32&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC32&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        tools:text=&amp;quot;Your Grade Is: A&amp;quot; /&amp;gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L33&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;33&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC33&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L34&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;34&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC34&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    &amp;lt;androidx.appcompat.widget.AppCompatButton&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L35&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;35&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC35&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:id=&amp;quot;@+id/btSubmit&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L36&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;36&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC36&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:layout_width=&amp;quot;0dp&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L37&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;37&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC37&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:layout_height=&amp;quot;wrap_content&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L38&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;38&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC38&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        android:text=&amp;quot;@string/submit&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L39&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;39&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC39&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        app:layout_constraintBottom_toBottomOf=&amp;quot;parent&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L40&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;40&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC40&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        app:layout_constraintEnd_toEndOf=&amp;quot;parent&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L41&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;41&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC41&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        app:layout_constraintStart_toStartOf=&amp;quot;parent&amp;quot; /&amp;gt;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L42&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;42&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC42&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-L43&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;43&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-activity_grade_calculator-xml-LC43&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;&amp;lt;/androidx.constraintlayout.widget.ConstraintLayout&amp;gt;&lt;/td&gt;
        &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;


    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;

      &lt;/div&gt;
      &lt;div class=&quot;gist-meta&quot;&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0/raw/dfe4006ce0cc8518371be19b4ceb35d811a082db/activity_grade_calculator.xml&quot; style=&quot;float:right&quot; class=&quot;Link--inTextBlock&quot;&gt;view raw&lt;/a&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0#file-activity_grade_calculator-xml&quot; class=&quot;Link--inTextBlock&quot;&gt;
          activity_grade_calculator.xml
        &lt;/a&gt;
        hosted with &amp;#10084; by &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Now, the test will compile but the test will fail because we have not provided any business logic yet.&lt;/p&gt;
&lt;h3&gt;Adding The Business Logic&lt;/h3&gt;
&lt;p&gt;The next step is to provide the business logic to calculate the grade.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Since there is nothing new about this part, I will just copy the tests and resulting logic from the previous post. You can read the &lt;a href=&quot;/android-spec-testing/&quot;&gt;Kotest post&lt;/a&gt;{:target=&quot;_blank&quot;} to understand better.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Create a &lt;code&gt;GradeCalculatorSpec&lt;/code&gt; class inside the test package and add the specs for grade calculation:&lt;/p&gt;
&lt;p&gt;&lt;div id=&quot;gist101813537&quot; class=&quot;gist&quot;&gt;
    &lt;div class=&quot;gist-file&quot; translate=&quot;no&quot; data-color-mode=&quot;light&quot; data-light-theme=&quot;light&quot;&gt;
      &lt;div class=&quot;gist-data&quot;&gt;
        
&lt;div class=&quot;js-gist-file-update-container js-task-list-container&quot;&gt;
      &lt;div id=&quot;file-gradecalculatorspec-kt&quot; class=&quot;file my-2&quot;&gt;
    
    &lt;div itemprop=&quot;text&quot;
      class=&quot;Box-body p-0 blob-wrapper data type-kotlin  &quot;
      style=&quot;overflow: auto&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;GradeCalculatorSpec.kt content, created by xuhaibahmad on 05:11AM on March 15, 2020.&quot;
    &gt;

        
&lt;div class=&quot;js-check-hidden-unicode js-blob-code-container blob-code-content&quot;&gt;

  &lt;template class=&quot;js-file-alert-template&quot;&gt;
  &lt;div data-view-component=&quot;true&quot; class=&quot;flash flash-warn flash-full d-flex flex-items-center&quot;&gt;
  &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
    &lt;span&gt;
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.co/hiddenchars&quot; target=&quot;_blank&quot;&gt;Learn more about bidirectional Unicode characters&lt;/a&gt;
    &lt;/span&gt;


  &lt;div data-view-component=&quot;true&quot; class=&quot;flash-action&quot;&gt;        &lt;a href=&quot;{{ revealButtonHref }}&quot; data-view-component=&quot;true&quot; class=&quot;btn-sm btn&quot;&gt;    Show hidden characters
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/template&gt;
&lt;template class=&quot;js-line-alert-template&quot;&gt;
  &lt;span aria-label=&quot;This line has hidden Unicode characters&quot; data-view-component=&quot;true&quot; class=&quot;line-alert tooltipped tooltipped-e&quot;&gt;
    &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/span&gt;&lt;/template&gt;

  &lt;table data-hpc class=&quot;highlight tab-size js-file-line-container&quot; data-tab-size=&quot;4&quot; data-paste-markdown-skip data-tagsearch-path=&quot;GradeCalculatorSpec.kt&quot;&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L1&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;1&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC1&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;class GradeCalculatorSpec : BehaviorSpec({&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L2&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;2&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC2&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L3&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;3&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC3&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    Given(&amp;quot;a grade calculator&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L4&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;4&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC4&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        val calculator = spyk(GradeCalculator())&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L5&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;5&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC5&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L6&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;6&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC6&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        every { calculator.totalMarks } returns 100&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L7&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;7&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC7&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        val total = calculator.totalMarks&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L8&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;8&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC8&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L9&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;9&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC9&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        When(&amp;quot;obtained marks are 90 or above&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L10&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;10&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC10&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            val grade = calculator.getGrade(93, total)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L11&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;11&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC11&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L12&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;12&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC12&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            Then(&amp;quot;grade is A&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L13&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;13&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC13&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                grade.shouldBe(&amp;quot;A&amp;quot;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L14&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;14&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC14&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L15&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;15&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC15&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L16&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;16&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC16&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        When(&amp;quot;obtained marks are between 80 and 89&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L17&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;17&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC17&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            val grade = calculator.getGrade(88, total)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L18&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;18&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC18&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L19&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;19&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC19&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            Then(&amp;quot;grade is B&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L20&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;20&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC20&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                grade.shouldBe(&amp;quot;B&amp;quot;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L21&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;21&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC21&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L22&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;22&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC22&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L23&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;23&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC23&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        When(&amp;quot;obtained marks are between 70 and 79&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L24&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;24&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC24&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            val grade = calculator.getGrade(78, total)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L25&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;25&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC25&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L26&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;26&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC26&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            Then(&amp;quot;grade is C&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L27&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;27&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC27&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                grade.shouldBe(&amp;quot;C&amp;quot;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L28&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;28&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC28&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L29&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;29&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC29&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L30&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;30&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC30&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        When(&amp;quot;obtained marks are between 60 and 69&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L31&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;31&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC31&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            val grade = calculator.getGrade(68, total)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L32&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;32&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC32&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L33&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;33&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC33&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            Then(&amp;quot;grade is D&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L34&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;34&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC34&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                grade.shouldBe(&amp;quot;D&amp;quot;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L35&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;35&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC35&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L36&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;36&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC36&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L37&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;37&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC37&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        When(&amp;quot;obtained marks are below 60&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L38&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;38&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC38&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            val grade = calculator.getGrade(59, total)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L39&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;39&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC39&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            Then(&amp;quot;grade is F&amp;quot;) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L40&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;40&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC40&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                grade.shouldBe(&amp;quot;F&amp;quot;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L41&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;41&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC41&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L42&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;42&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC42&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L43&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;43&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC43&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-L44&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;44&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatorspec-kt-LC44&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;})&lt;/td&gt;
        &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;


    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;

      &lt;/div&gt;
      &lt;div class=&quot;gist-meta&quot;&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0/raw/dfe4006ce0cc8518371be19b4ceb35d811a082db/GradeCalculatorSpec.kt&quot; style=&quot;float:right&quot; class=&quot;Link--inTextBlock&quot;&gt;view raw&lt;/a&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0#file-gradecalculatorspec-kt&quot; class=&quot;Link--inTextBlock&quot;&gt;
          GradeCalculatorSpec.kt
        &lt;/a&gt;
        hosted with &amp;#10084; by &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Once all the tests are passing, the resulting implementation would look something like this:&lt;/p&gt;
&lt;p&gt;&lt;div id=&quot;gist101813537&quot; class=&quot;gist&quot;&gt;
    &lt;div class=&quot;gist-file&quot; translate=&quot;no&quot; data-color-mode=&quot;light&quot; data-light-theme=&quot;light&quot;&gt;
      &lt;div class=&quot;gist-data&quot;&gt;
        
&lt;div class=&quot;js-gist-file-update-container js-task-list-container&quot;&gt;
      &lt;div id=&quot;file-gradecalculator-kt&quot; class=&quot;file my-2&quot;&gt;
    
    &lt;div itemprop=&quot;text&quot;
      class=&quot;Box-body p-0 blob-wrapper data type-kotlin  &quot;
      style=&quot;overflow: auto&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;GradeCalculator.kt content, created by xuhaibahmad on 05:11AM on March 15, 2020.&quot;
    &gt;

        
&lt;div class=&quot;js-check-hidden-unicode js-blob-code-container blob-code-content&quot;&gt;

  &lt;template class=&quot;js-file-alert-template&quot;&gt;
  &lt;div data-view-component=&quot;true&quot; class=&quot;flash flash-warn flash-full d-flex flex-items-center&quot;&gt;
  &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
    &lt;span&gt;
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.co/hiddenchars&quot; target=&quot;_blank&quot;&gt;Learn more about bidirectional Unicode characters&lt;/a&gt;
    &lt;/span&gt;


  &lt;div data-view-component=&quot;true&quot; class=&quot;flash-action&quot;&gt;        &lt;a href=&quot;{{ revealButtonHref }}&quot; data-view-component=&quot;true&quot; class=&quot;btn-sm btn&quot;&gt;    Show hidden characters
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/template&gt;
&lt;template class=&quot;js-line-alert-template&quot;&gt;
  &lt;span aria-label=&quot;This line has hidden Unicode characters&quot; data-view-component=&quot;true&quot; class=&quot;line-alert tooltipped tooltipped-e&quot;&gt;
    &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/span&gt;&lt;/template&gt;

  &lt;table data-hpc class=&quot;highlight tab-size js-file-line-container&quot; data-tab-size=&quot;4&quot; data-paste-markdown-skip data-tagsearch-path=&quot;GradeCalculator.kt&quot;&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L1&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;1&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC1&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;class GradeCalculator {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L2&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;2&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC2&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L3&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;3&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC3&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    var totalMarks = 0&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L4&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;4&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC4&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L5&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;5&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC5&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    fun getGrade(obtainedMarks: Int, totalMarks: Int): String {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L6&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;6&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC6&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        val percentage = getPercentage(obtainedMarks, totalMarks)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L7&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;7&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC7&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        return when {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L8&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;8&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC8&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            percentage &amp;gt;= 90 -&amp;gt; &amp;quot;A&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L9&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;9&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC9&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            percentage in 80..89 -&amp;gt; &amp;quot;B&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L10&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;10&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC10&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            percentage in 70..79 -&amp;gt; &amp;quot;C&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L11&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;11&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC11&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            percentage in 60..69 -&amp;gt; &amp;quot;D&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L12&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;12&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC12&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            else -&amp;gt; &amp;quot;F&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L13&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;13&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC13&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L14&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;14&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC14&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L15&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;15&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC15&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L16&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;16&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC16&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    private fun getPercentage(obtainedMarks: Int, totalMarks: Int): Int {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L17&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;17&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC17&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        return  (obtainedMarks / totalMarks.toFloat() * 100).roundToInt()&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L18&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;18&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC18&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculator-kt-L19&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;19&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculator-kt-LC19&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;}&lt;/td&gt;
        &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;


    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;

      &lt;/div&gt;
      &lt;div class=&quot;gist-meta&quot;&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0/raw/dfe4006ce0cc8518371be19b4ceb35d811a082db/GradeCalculator.kt&quot; style=&quot;float:right&quot; class=&quot;Link--inTextBlock&quot;&gt;view raw&lt;/a&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0#file-gradecalculator-kt&quot; class=&quot;Link--inTextBlock&quot;&gt;
          GradeCalculator.kt
        &lt;/a&gt;
        hosted with &amp;#10084; by &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;h3&gt;Binding It All Together&lt;/h3&gt;
&lt;p&gt;The final step is to bind the &lt;code&gt;GradeCalculator&lt;/code&gt; with the UI. Let&apos;s add a click listener to the button and use the user input to calculate the grade.&lt;/p&gt;
&lt;p&gt;&lt;div id=&quot;gist101813537&quot; class=&quot;gist&quot;&gt;
    &lt;div class=&quot;gist-file&quot; translate=&quot;no&quot; data-color-mode=&quot;light&quot; data-light-theme=&quot;light&quot;&gt;
      &lt;div class=&quot;gist-data&quot;&gt;
        
&lt;div class=&quot;js-gist-file-update-container js-task-list-container&quot;&gt;
      &lt;div id=&quot;file-gradecalculatoractivity-kt&quot; class=&quot;file my-2&quot;&gt;
    
    &lt;div itemprop=&quot;text&quot;
      class=&quot;Box-body p-0 blob-wrapper data type-kotlin  &quot;
      style=&quot;overflow: auto&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;GradeCalculatorActivity.kt content, created by xuhaibahmad on 05:11AM on March 15, 2020.&quot;
    &gt;

        
&lt;div class=&quot;js-check-hidden-unicode js-blob-code-container blob-code-content&quot;&gt;

  &lt;template class=&quot;js-file-alert-template&quot;&gt;
  &lt;div data-view-component=&quot;true&quot; class=&quot;flash flash-warn flash-full d-flex flex-items-center&quot;&gt;
  &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
    &lt;span&gt;
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.co/hiddenchars&quot; target=&quot;_blank&quot;&gt;Learn more about bidirectional Unicode characters&lt;/a&gt;
    &lt;/span&gt;


  &lt;div data-view-component=&quot;true&quot; class=&quot;flash-action&quot;&gt;        &lt;a href=&quot;{{ revealButtonHref }}&quot; data-view-component=&quot;true&quot; class=&quot;btn-sm btn&quot;&gt;    Show hidden characters
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/template&gt;
&lt;template class=&quot;js-line-alert-template&quot;&gt;
  &lt;span aria-label=&quot;This line has hidden Unicode characters&quot; data-view-component=&quot;true&quot; class=&quot;line-alert tooltipped tooltipped-e&quot;&gt;
    &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/span&gt;&lt;/template&gt;

  &lt;table data-hpc class=&quot;highlight tab-size js-file-line-container&quot; data-tab-size=&quot;4&quot; data-paste-markdown-skip data-tagsearch-path=&quot;GradeCalculatorActivity.kt&quot;&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L1&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;1&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC1&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;class GradeCalculatorActivity : AppCompatActivity() {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L2&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;2&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC2&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L3&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;3&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC3&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    companion object {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L4&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;4&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC4&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        private const val TOTAL_MARKS = 100&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L5&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;5&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC5&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L6&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;6&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC6&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L7&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;7&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC7&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    private val gradeCalculator = GradeCalculator()&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L8&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;8&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC8&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;
&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L9&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;9&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC9&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    override fun onCreate(savedInstanceState: Bundle?) {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L10&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;10&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC10&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        super.onCreate(savedInstanceState)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L11&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;11&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC11&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        setContentView(R.layout.activity_grade_calculator)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L12&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;12&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC12&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        btSubmit.setOnClickListener {&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L13&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;13&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC13&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            val inputMarks = etMarks.text.toString().toInt()&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L14&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;14&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC14&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            val grade = gradeCalculator.getGrade(inputMarks, TOTAL_MARKS)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L15&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;15&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC15&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            tvGrade.text = &amp;quot;Your Grade is: $grade&amp;quot;&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L16&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;16&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC16&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L17&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;17&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC17&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;    }&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-L18&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;18&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-gradecalculatoractivity-kt-LC18&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;}&lt;/td&gt;
        &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;


    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;

      &lt;/div&gt;
      &lt;div class=&quot;gist-meta&quot;&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0/raw/dfe4006ce0cc8518371be19b4ceb35d811a082db/GradeCalculatorActivity.kt&quot; style=&quot;float:right&quot; class=&quot;Link--inTextBlock&quot;&gt;view raw&lt;/a&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0#file-gradecalculatoractivity-kt&quot; class=&quot;Link--inTextBlock&quot;&gt;
          GradeCalculatorActivity.kt
        &lt;/a&gt;
        hosted with &amp;#10084; by &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;If all goes well, all tests should be passing now. Also, if you look at the logcat output, it will have a complete summary of all test steps.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Bonus: If there was no issue with permission, it should have also captured the screenshots for each step automagically! Look inside the gallery for them.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Here&apos;s the pidcat output of my tests:&lt;/p&gt;
&lt;p&gt;&lt;div id=&quot;gist101813537&quot; class=&quot;gist&quot;&gt;
    &lt;div class=&quot;gist-file&quot; translate=&quot;no&quot; data-color-mode=&quot;light&quot; data-light-theme=&quot;light&quot;&gt;
      &lt;div class=&quot;gist-data&quot;&gt;
        
&lt;div class=&quot;js-gist-file-update-container js-task-list-container&quot;&gt;
      &lt;div id=&quot;file-testoutput-txt&quot; class=&quot;file my-2&quot;&gt;
    
    &lt;div itemprop=&quot;text&quot;
      class=&quot;Box-body p-0 blob-wrapper data type-text  &quot;
      style=&quot;overflow: auto&quot; tabindex=&quot;0&quot; role=&quot;region&quot;
      aria-label=&quot;TestOutput.txt content, created by xuhaibahmad on 05:11AM on March 15, 2020.&quot;
    &gt;

        
&lt;div class=&quot;js-check-hidden-unicode js-blob-code-container blob-code-content&quot;&gt;

  &lt;template class=&quot;js-file-alert-template&quot;&gt;
  &lt;div data-view-component=&quot;true&quot; class=&quot;flash flash-warn flash-full d-flex flex-items-center&quot;&gt;
  &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
    &lt;span&gt;
      This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
      &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.co/hiddenchars&quot; target=&quot;_blank&quot;&gt;Learn more about bidirectional Unicode characters&lt;/a&gt;
    &lt;/span&gt;


  &lt;div data-view-component=&quot;true&quot; class=&quot;flash-action&quot;&gt;        &lt;a href=&quot;{{ revealButtonHref }}&quot; data-view-component=&quot;true&quot; class=&quot;btn-sm btn&quot;&gt;    Show hidden characters
&lt;/a&gt;
&lt;/div&gt;
&lt;/div&gt;&lt;/template&gt;
&lt;template class=&quot;js-line-alert-template&quot;&gt;
  &lt;span aria-label=&quot;This line has hidden Unicode characters&quot; data-view-component=&quot;true&quot; class=&quot;line-alert tooltipped tooltipped-e&quot;&gt;
    &lt;svg aria-hidden=&quot;true&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot; version=&quot;1.1&quot; width=&quot;16&quot; data-view-component=&quot;true&quot; class=&quot;octicon octicon-alert&quot;&gt;
    &lt;path d=&quot;M6.457 1.047c.659-1.234 2.427-1.234 3.086 0l6.082 11.378A1.75 1.75 0 0 1 14.082 15H1.918a1.75 1.75 0 0 1-1.543-2.575Zm1.763.707a.25.25 0 0 0-.44 0L1.698 13.132a.25.25 0 0 0 .22.368h12.164a.25.25 0 0 0 .22-.368Zm.53 3.996v2.5a.75.75 0 0 1-1.5 0v-2.5a.75.75 0 0 1 1.5 0ZM9 11a1 1 0 1 1-2 0 1 1 0 0 1 2 0Z&quot;&gt;&lt;/path&gt;
&lt;/svg&gt;
&lt;/span&gt;&lt;/template&gt;

  &lt;table data-hpc class=&quot;highlight tab-size js-file-line-container&quot; data-tab-size=&quot;4&quot; data-paste-markdown-skip data-tagsearch-path=&quot;TestOutput.txt&quot;&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L1&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;1&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC1&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt; KASPRESSO  I  ---------------------------------------------------------------------------&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L2&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;2&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC2&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  BEFORE TEST SECTION&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L3&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;3&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC3&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  ---------------------------------------------------------------------------&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L4&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;4&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC4&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  ---------------------------------------------------------------------------&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L5&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;5&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC5&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  TEST SECTION&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L6&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;6&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC6&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  ---------------------------------------------------------------------------&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L7&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;7&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC7&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  ___________________________________________________________________________&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L8&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;8&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC8&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  TEST STEP: &amp;quot;1. Open grade calculator screen&amp;quot; in GradeCalculatorUiTest&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L9&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;9&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC9&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;       ActivityTestRule  W  getActivityIntent() returned null using default: Intent(Intent.ACTION_MAIN)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L10&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;10&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC10&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;         ActivityThread  W  handleWindowVisibility: no activity for token android.os.BinderProxy@efc119d&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L11&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;11&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC11&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;       LifecycleMonitor  D  Lifecycle status change: com.zuhaibahmad.kaspressotestingdemo.GradeCalculatorActivity@76bf15e in: PRE_ON_CREATE&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L12&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;12&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC12&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  running callback: com.kaspersky.kaspresso.device.languages.LanguageImpl$lifecycleCallback$1@934993f&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L13&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;13&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC13&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  callback completes: com.kaspersky.kaspresso.device.languages.LanguageImpl$lifecycleCallback$1@934993f&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L14&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;14&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC14&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;         essotestingdem  W  Accessing hidden method Landroid/graphics/drawable/Drawable;-&amp;gt;getOpticalInsets()Landroid/graphics/Insets; (light greylist, linking)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L15&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;15&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC15&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden field Landroid/graphics/Insets;-&amp;gt;left:I (light greylist, linking)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L16&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;16&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC16&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden field Landroid/graphics/Insets;-&amp;gt;right:I (light greylist, linking)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L17&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;17&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC17&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden field Landroid/graphics/Insets;-&amp;gt;top:I (light greylist, linking)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L18&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;18&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC18&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden field Landroid/graphics/Insets;-&amp;gt;bottom:I (light greylist, linking)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L19&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;19&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC19&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden method Landroid/view/View;-&amp;gt;getAccessibilityDelegate()Landroid/view/View$AccessibilityDelegate; (light greylist, linking)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L20&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;20&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC20&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden method Landroid/view/View;-&amp;gt;computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (light greylist, reflect&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L21&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;21&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC21&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                            ion)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L22&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;22&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC22&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden method Landroid/view/ViewGroup;-&amp;gt;makeOptionalFitsSystemWindows()V (light greylist, reflection)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L23&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;23&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC23&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden method Landroid/widget/TextView;-&amp;gt;getTextDirectionHeuristic()Landroid/text/TextDirectionHeuristic; (light greylist, linking)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L24&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;24&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC24&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;       LifecycleMonitor  D  Lifecycle status change: com.zuhaibahmad.kaspressotestingdemo.GradeCalculatorActivity@76bf15e in: CREATED&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L25&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;25&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC25&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  running callback: com.kaspersky.kaspresso.device.languages.LanguageImpl$lifecycleCallback$1@934993f&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L26&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;26&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC26&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  callback completes: com.kaspersky.kaspresso.device.languages.LanguageImpl$lifecycleCallback$1@934993f&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L27&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;27&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC27&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  Lifecycle status change: com.zuhaibahmad.kaspressotestingdemo.GradeCalculatorActivity@76bf15e in: STARTED&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L28&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;28&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC28&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  running callback: com.kaspersky.kaspresso.device.languages.LanguageImpl$lifecycleCallback$1@934993f&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L29&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;29&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC29&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  callback completes: com.kaspersky.kaspresso.device.languages.LanguageImpl$lifecycleCallback$1@934993f&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L30&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;30&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC30&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  Lifecycle status change: com.zuhaibahmad.kaspressotestingdemo.GradeCalculatorActivity@76bf15e in: RESUMED&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L31&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;31&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC31&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  running callback: com.kaspersky.kaspresso.device.languages.LanguageImpl$lifecycleCallback$1@934993f&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L32&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;32&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC32&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  callback completes: com.kaspersky.kaspresso.device.languages.LanguageImpl$lifecycleCallback$1@934993f&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L33&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;33&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC33&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;         OpenGLRenderer  D  HWUI GL Pipeline&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L34&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;34&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC34&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;         HostConnection  D  HostConnection::get() New Host Connection established 0xe8b21370, tid 6867&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L35&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;35&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC35&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  HostComposition ext ANDROID_EMU_CHECKSUM_HELPER_v1 ANDROID_EMU_dma_v1 ANDROID_EMU_YUV420_888_to_NV21 ANDROID_EMU_YUV_Cache ANDROID_EMU_async_u&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L36&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;36&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC36&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                            nmap_buffer GL_OES_vertex_array_object GL_KHR_texture_compression_astc_ldr ANDROID_EMU_gles_max_version_2&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L37&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;37&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC37&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;            ConfigStore  I  android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasWideColorDisplay retrieved: 0&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L38&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;38&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC38&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  android::hardware::configstore::V1_0::ISurfaceFlingerConfigs::hasHDRDisplay retrieved: 0&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L39&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;39&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC39&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;         OpenGLRenderer  I  Initialized EGL, version 1.4&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L40&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;40&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC40&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  Swap behavior 1&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L41&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;41&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC41&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L42&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;42&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC42&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  Swap behavior 0&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L43&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;43&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC43&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;         eglCodecCommon  D  setVertexArrayObject: set vao to 0 (0) 0 0&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L44&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;44&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC44&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;          EGL_emulation  D  eglCreateContext: 0xe8b05420: maj 2 min 0 rcv 2&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L45&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;45&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC45&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  eglMakeCurrent: 0xe8b05420: ver 2 0 (tinfo 0xe8b03790)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L46&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;46&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC46&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;         HostConnection  D  createUnique: call&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L47&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;47&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC47&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  HostConnection::get() New Host Connection established 0xe8b215f0, tid 6867&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L48&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;48&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC48&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         D  HostComposition ext ANDROID_EMU_CHECKSUM_HELPER_v1 ANDROID_EMU_dma_v1 ANDROID_EMU_YUV420_888_to_NV21 ANDROID_EMU_YUV_Cache ANDROID_EMU_async_u&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L49&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;49&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC49&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                            nmap_buffer GL_OES_vertex_array_object GL_KHR_texture_compression_astc_ldr ANDROID_EMU_gles_max_version_2&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L50&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;50&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC50&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;         eglCodecCommon  E  GoldfishAddressSpaceHostMemoryAllocator: ioctl_ping failed for device_type=5, ret=-1&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L51&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;51&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC51&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;          EGL_emulation  D  eglMakeCurrent: 0xe8b05420: ver 2 0 (tinfo 0xe8b03790)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L52&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;52&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC52&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        AssistStructure  I  Flattened final assist data: 2688 bytes, containing 1 windows, 9 views&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L53&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;53&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC53&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;              KASPRESSO  I  TEST STEP: &amp;quot;1. Open grade calculator screen&amp;quot; in GradeCalculatorUiTest SUCCEED. It took 0 minutes, 0 seconds and 339 millis.&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L54&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;54&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC54&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  ___________________________________________________________________________&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L55&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;55&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC55&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;              KASPRESSO  I  ___________________________________________________________________________&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L56&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;56&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC56&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  TEST STEP: &amp;quot;2. Submit obtained marks&amp;quot; in GradeCalculatorUiTest&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L57&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;57&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC57&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;         essotestingdem  W  Accessing hidden method Landroid/os/MessageQueue;-&amp;gt;next()Landroid/os/Message; (light greylist, reflection)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L58&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;58&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC58&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden field Landroid/os/MessageQueue;-&amp;gt;mMessages:Landroid/os/Message; (light greylist, reflection)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L59&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;59&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC59&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden method Landroid/os/Message;-&amp;gt;recycleUnchecked()V (light greylist, reflection)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L60&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;60&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC60&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden method Landroid/view/WindowManagerGlobal;-&amp;gt;getInstance()Landroid/view/WindowManagerGlobal; (light greylist, reflection)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L61&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;61&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC61&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden field Landroid/view/WindowManagerGlobal;-&amp;gt;mViews:Ljava/util/ArrayList; (light greylist, reflection)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L62&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;62&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC62&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         W  Accessing hidden field Landroid/view/WindowManagerGlobal;-&amp;gt;mParams:Ljava/util/ArrayList; (light greylist, reflection)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L63&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;63&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC63&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        ViewInteraction  I  Performing &amp;#39;replace text(90)&amp;#39; action on view (with id: com.zuhaibahmad.kaspressotestingdemo:id/etMarks)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L64&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;64&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC64&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;              KASPRESSO  I  replace text(90) on AppCompatEditText(id=etMarks;hint=Enter Your Marks;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L65&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;65&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC65&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;         essotestingdem  W  Accessing hidden method Landroid/view/ViewConfiguration;-&amp;gt;getDoubleTapMinTime()I (light greylist, reflection)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L66&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;66&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC66&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        ViewInteraction  I  Performing &amp;#39;single click&amp;#39; action on view (with id: com.zuhaibahmad.kaspressotestingdemo:id/btSubmit)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L67&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;67&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC67&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;              KASPRESSO  I  single click on AppCompatButton(id=btSubmit;text=Submit;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L68&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;68&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC68&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  TEST STEP: &amp;quot;2. Submit obtained marks&amp;quot; in GradeCalculatorUiTest SUCCEED. It took 0 minutes, 0 seconds and 315 millis.&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L69&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;69&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC69&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  ___________________________________________________________________________&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L70&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;70&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC70&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;              KASPRESSO  I  ___________________________________________________________________________&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L71&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;71&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC71&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  TEST STEP: &amp;quot;3. Verify the displayed grade is correct&amp;quot; in GradeCalculatorUiTest&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L72&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;72&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC72&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;        ViewInteraction  I  Checking &amp;#39;com.kaspersky.kaspresso.proxy.ViewAssertionProxy@d4df345&amp;#39; assertion on view (with id: com.zuhaibahmad.kaspressotestingdemo:id/tvGrad&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L73&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;73&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC73&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                            e)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L74&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;74&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC74&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;              KASPRESSO  I  Check with text: is &amp;quot;Your Grade is: A&amp;quot; on AppCompatTextView(id=tvGrade;text=Your Grade is: A;)&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L75&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;75&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC75&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  TEST STEP: &amp;quot;3. Verify the displayed grade is correct&amp;quot; in GradeCalculatorUiTest SUCCEED. It took 0 minutes, 0 seconds and 5 millis.&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L76&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;76&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC76&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  ___________________________________________________________________________&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L77&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;77&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC77&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;              KASPRESSO  I  ---------------------------------------------------------------------------&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L78&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;78&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC78&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  AFTER TEST SECTION&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L79&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;79&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC79&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  ---------------------------------------------------------------------------&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L80&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;80&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC80&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  ---------------------------------------------------------------------------&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L81&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;81&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC81&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  TEST PASSED&lt;/td&gt;
        &lt;/tr&gt;
        &lt;tr&gt;
          &lt;td id=&quot;file-testoutput-txt-L82&quot; class=&quot;blob-num js-line-number js-blob-rnum&quot; data-line-number=&quot;82&quot;&gt;&lt;/td&gt;
          &lt;td id=&quot;file-testoutput-txt-LC82&quot; class=&quot;blob-code blob-code-inner js-file-line&quot;&gt;                         I  ---------------------------------------------------------------------------&lt;/td&gt;
        &lt;/tr&gt;
  &lt;/table&gt;
&lt;/div&gt;


    &lt;/div&gt;

  &lt;/div&gt;

&lt;/div&gt;

      &lt;/div&gt;
      &lt;div class=&quot;gist-meta&quot;&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0/raw/dfe4006ce0cc8518371be19b4ceb35d811a082db/TestOutput.txt&quot; style=&quot;float:right&quot; class=&quot;Link--inTextBlock&quot;&gt;view raw&lt;/a&gt;
        &lt;a href=&quot;https://gist.github.com/xuhaibahmad/71d78d0048162b9fd744387c3dedcee0#file-testoutput-txt&quot; class=&quot;Link--inTextBlock&quot;&gt;
          TestOutput.txt
        &lt;/a&gt;
        hosted with &amp;#10084; by &lt;a class=&quot;Link--inTextBlock&quot; href=&quot;https://github.com&quot;&gt;GitHub&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;&lt;/p&gt;
&lt;p&gt;Using Kaspresso, you can also handle runtime permissions, change device state, execute ADB commands right from your code. The flexibility it provides for writing tests in truly amazing.&lt;/p&gt;
&lt;hr&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;This is a very basic example that does not handle any edge cases. However, I hope it was enough to give you a taste of test-driven development with Android.&lt;/p&gt;
&lt;p&gt;We started with a crude idea without any implementation details in mind and used tests to shape our logic. There are a couple of benefits to this approach:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;We ended up with a minimum workable solution with 100% code coverage&lt;/li&gt;
&lt;li&gt;As every part of our logic is backed by Unit and UI tests, the tests themselves serve as a documentation for the features.&lt;/li&gt;
&lt;li&gt;Since both the architecture and the logic is shaped by the tests themselves, it is quite easy to freely refactor the code further without worrying about regression bugs.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;You can find complete source code for this post &lt;a href=&quot;https://github.com/xuhaibahmad/Kaspresso-Testing-Demo&quot;&gt;here&lt;/a&gt;{:target=&quot;_blank&quot;}&lt;/p&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Painless Unit Testing with Kotlintest & Mockk]]></title><description><![CDATA[Testing is hard and not everyone likes to spend their time writing unit tests when they could be building shiny new features. While almost…]]></description><link>https://www.zuhaibahmad.com/android-spec-testing/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/android-spec-testing/</guid><pubDate>Sat, 07 Sep 2019 11:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Testing is hard and not everyone likes to spend their time writing unit tests when they could be building shiny new features.&lt;/p&gt;
&lt;p&gt;While almost every developer understands the value of testing their code, most of us fall prey of laziness in the face of approaching deadlines and prospect of more exciting work.&lt;/p&gt;
&lt;p&gt;In this post, let&apos;s understand why unit tests serve as the backbone of successful products and learn a new way of writing tests that is much simpler, intuitive and appealing.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Why Testing Matters&lt;/h2&gt;
&lt;p&gt;Since I mostly work with Android applications. I&apos;ll let you in on a little secret. More than half of the code bases you will come across will have little to no tests. Not just the crappy ones, but sometimes products with thousands of users.&lt;/p&gt;
&lt;p&gt;This is often due to the complex and dull nature of old school &lt;a href=&quot;https://junit.org&quot;&gt;JUnit&lt;/a&gt; based testing. Not only the process is quite boring, it also requires a lot of repetitive boilerplate code to be written for even the most simplest scenarios, making testing a very time consuming process.&lt;/p&gt;
&lt;p&gt;Obviously, when you are in a rush to take the product out as soon as possible, testing becomes least desirable practice.&lt;/p&gt;
&lt;p&gt;More often than not, the end result is that project becomes unmaintainable within a year. I have seen organizations rewriting already published products only because the code base has become unmaintainable due to spaghetti code that is rotten with bugs.&lt;/p&gt;
&lt;p&gt;Obviously this is not a desirable situation. Testing your code for bugs and architectural flaws from the beginning solidifies your product to be manageable in the longer run.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;If you can not measure it, you can not improve it.&quot; - Lord Kelvin&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Testing As You Go&lt;/h2&gt;
&lt;p&gt;Laying the foundations of your project with extensive testing is not a new idea. &lt;a href=&quot;https://en.wikipedia.org/wiki/Behavior-driven_development&quot;&gt;Behavior Driven Development&lt;/a&gt; has been around for decades.&lt;/p&gt;
&lt;p&gt;The practice is to turns feature specifications (or user stories) into a set of unit tests and then writing code that satisfies those specs via passing tests.&lt;/p&gt;
&lt;p&gt;This results in co-development of both your features and their tests side by side. Coupled with a Continuous Integration solution, this setup almost guarantees that you will never have any regression bugs.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Specification Based Testing In Practice&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://cucumber.io/&quot;&gt;Cucumber&lt;/a&gt; is the most widely used BDD testing framework right now. It provides a plain language parser called Gherkin which can be used to write tests in plain English that non-programmers can also understand. The framework turns these specifications into acceptance tests which also serves as the documentation for each feature. Here is what a test written in Gherkin looks like:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;gherkin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Feature: Calculator
As a user
I want to use a calculator to add numbers
So that I don&amp;#39;t need to add myself

Scenario: Add two numbers -2 &amp;amp; 3
Given I have a calculator
When I add -2 and 3
Then the result should be 1

Scenario: Add two numbers 10 &amp;amp; 15
Given I have a calculator
When I add 10 and 15
Then the result should be 25&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Neat! Isn&apos;t it? This spec results in unit tests that run with Cucumber. Since this post is not about Cucumber, I won&apos;t go into the implementation details. You can visit this &lt;a href=&quot;https://www.baeldung.com/cucumber-scenario-outline&quot;&gt;tutorial&lt;/a&gt;{:target=&quot;_blank&quot;} for full tutorial on BDD style testing with Cucumber. I used the same article to borrow the above example.&lt;/p&gt;
&lt;p&gt;Some other popular BDD frameworks include &lt;a href=&quot;http://lettuce.it/&quot;&gt;Lettuce&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;http://jasmine.github.io&quot;&gt;Jasmine&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;http://www.specflow.org/&quot;&gt;SpecFlow&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;https://spekframework.org/&quot;&gt;Spek&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;http://behat.org&quot;&gt;Behat&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;https://github.com/jdave/Jdave&quot;&gt;Jdave&lt;/a&gt;{:target=&quot;_blank&quot;} and &lt;a href=&quot;http://jbehave.org/&quot;&gt;Jbehave&lt;/a&gt;{:target=&quot;_blank&quot;}&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Problems with JUnit&lt;/h2&gt;
&lt;p&gt;JUnit is a industry standard at this point for all things Java. It has ruled the Java development for decades now and it works. But since it was built for Java, it brings with it most of the common pitfalls of Java based technologies that feel backwards, specially in the world of modern programming languages like Kotlin, JavaScript and Python.&lt;/p&gt;
&lt;p&gt;Having said that, writing all styles of tests are entirely possible with JUnit. &lt;a href=&quot;https://site.mockito.org&quot;&gt;Mockito&lt;/a&gt; provides a BDD style extension in its core library that allows you to do similar &lt;code&gt;Give, When, Then&lt;/code&gt; style testing within JUnit test cases.&lt;/p&gt;
&lt;p&gt;Let&apos;s take a look at some of the problems associated with it and why I believe vanilla JUnit is not a good combination for a powerful language like Kotlin:&lt;/p&gt;
&lt;h3&gt;Code Repetition&lt;/h3&gt;
&lt;p&gt;Like everything in Java, JUnit is also quite verbose. This results in too much repetition of similar contents with slight change of logic to test different scenarios.&lt;/p&gt;
&lt;p&gt;How many time have you had a test for a HAS and a HAS-NOT condition with all the same contents except for a boolean? For example, a &lt;code&gt;testAccessWhenHasToken&lt;/code&gt; and then &lt;code&gt;testAccessWhenNoToken&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Of course, you can stuff in all your assertions in one test at the cost of readability. However, a good test is supposed to be granular i.e. targeting one case per test.&lt;/p&gt;
&lt;p&gt;Also, your test code is guaranteed to grow as your project gets bigger and at some point your test code will surpass your application code. If you want to look at examples of this, try looking into the source code of any popular open source projects such as &lt;a href=&quot;https://github.com/ReactiveX/RxJava&quot;&gt;RxJava&lt;/a&gt;, &lt;a href=&quot;https://square.github.io/retrofit/&quot;&gt;Retrofit&lt;/a&gt;, &lt;a href=&quot;https://square.github.io/okhttp/&quot;&gt;OkHttp&lt;/a&gt;, &lt;a href=&quot;https://square.github.io/picasso/&quot;&gt;Picasso&lt;/a&gt; etc. Almost all of them have 1.5x to 2x more test code compared to their business logic.&lt;/p&gt;
&lt;h3&gt;Lack Of Contextual Information&lt;/h3&gt;
&lt;p&gt;If you are like me, then one of the first things that you may have done while switching to Kotlin was to change your long JUnit test names like &lt;code&gt;fun onTouchOutside_shouldDismissDialogAndResumeStreaming()&lt;/code&gt; with back tick notation. While a big improvement, it gave us a license to go wild with it in our attempts to provide more contextual information about each test case. So now our test case has evolved into something like this: &lt;code&gt;fun `on touch outside, dismiss dialog and resume streaming the paused song` ()&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;The real issue here is that these names are not enough to provide full context about the test. Unless you are willing to write entire paragraph to define the pre and post conditions for the scenario.&lt;/p&gt;
&lt;p&gt;Another problem here is with the organization of test cases. Most of the test cases have some shared code that can be logically structured into a hierarchy. However, because JUnit only allows you to write tests in the form of class methods. So you end up writing more tests, more code but with less context about overall theme of the current group of tests.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;A Better Way To Write Tests&lt;/h2&gt;
&lt;p&gt;The reason behind giving you a taste of Cucumber BDD was to show what it would be like to have such idiomatic tests.&lt;/p&gt;
&lt;p&gt;Let&apos;s define a set of specification that we want to build and test. We will be using two libraries &lt;a href=&quot;https://github.com/kotlintest/kotlintest&quot;&gt;Kotlintest&lt;/a&gt; and &lt;a href=&quot;https://mockk.io/&quot;&gt;Mockk&lt;/a&gt; to help us write tests.&lt;/p&gt;
&lt;h3&gt;Feature&lt;/h3&gt;
&lt;p&gt;Let&apos;s build a grade calculator that tells you your grade based on the marks you obtained. Here are the rules for grading:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When obtained marks are 90 or above, then grade is A.&lt;/li&gt;
&lt;li&gt;When obtained marks are between 80 and 89, then grade is B&lt;/li&gt;
&lt;li&gt;When obtained marks are between 70 and 79, then grade is C&lt;/li&gt;
&lt;li&gt;When obtained marks are between 60 and 69, then grade is D&lt;/li&gt;
&lt;li&gt;When obtained marks are below 60, then grade is F&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Installation&lt;/h3&gt;
&lt;p&gt;First, create an empty Kotlin or Android project and add the following two dependencies:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;groovy&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;testImplementation &amp;#39;io.kotlintest:kotlintest-runner-junit5:3.3.2&amp;#39;
testImplementation &amp;#39;io.mockk:mockk:1.9.3.kotlin12&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The first dependency is Kotlintest which is a testing library built on top of JUnit. It takes advantage of Kotlin&apos;s DSL capabilities to support various type of testing styles.&lt;/p&gt;
&lt;p&gt;Mockk is a Kotlin-based mocking library with a very clean syntax that blends really well with Kotlintest&apos;s specs. It also provides much more flexible API and much wider set of feature compared to Mockito or Powermock.&lt;/p&gt;
&lt;h3&gt;Implementation&lt;/h3&gt;
&lt;p&gt;Let&apos;s start by creating an empty &lt;code&gt;GradeCalculator&lt;/code&gt; class and converting our specifications into a Spec.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;class GradeCalculatorSpec : BehaviorSpec({

  Given(&amp;quot;a grade calculator&amp;quot;) {
    val calculator = spyk(GradeCalculator())

    every { calculator.totalMarks } returns 100
    val total = calculator.totalMarks

    When(&amp;quot;obtained marks are 90 or above&amp;quot;) {
      Then(&amp;quot;grade is A&amp;quot;) {}
    }
    When(&amp;quot;obtained marks are between 80-89&amp;quot;) {
      Then(&amp;quot;grade is B&amp;quot;) {}
    }
    When(&amp;quot;obtained marks are between 70-79&amp;quot;) {
      Then(&amp;quot;grade is C&amp;quot;) {}
    }
    When(&amp;quot;obtained marks are between 60-69&amp;quot;) {
      Then(&amp;quot;grade is D&amp;quot;) {}
    }
    When(&amp;quot;obtained marks are below 60&amp;quot;) {
      Then(&amp;quot;grade is F&amp;quot;) {}
    }
  }
})&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This does not quite look like our traditional JUnit test. So let&apos;s break it down and understand bit by bit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;BehaviorSpec&lt;/strong&gt; - We are extending something called a &lt;em&gt;BehaviorSpec&lt;/em&gt; which is basically a Spec written in BDD style (remember the Give, When, Then from Cucumber?). There are dozens of different other Spec styles available in Kotlintest.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Spyk&lt;/strong&gt; - You may notice that I wrapped the &lt;code&gt;GradeCalculator&lt;/code&gt; object with a &lt;code&gt;spyk&lt;/code&gt; method. If you have used Mockito before than the concept is the same. Basically, a spy is a wrapper that lets you mock some methods and variables of the object while using the actual values for the rest. I did it to demonstrate the use of Mockk here.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Every/Returns&lt;/strong&gt; - Similar to Mockito&apos;s &lt;code&gt;when/then&lt;/code&gt; style, this construct is used by Mockk to to prepare mock values. All we are saying is to return a mock value whenever anyone in this code block asks for this value. The value we are mocking is total marks which are going to help calculate the grade.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;When/Then&lt;/strong&gt; - Finally, there are a bunch of &lt;code&gt;Then&lt;/code&gt; blocks nested inside &lt;code&gt;When&lt;/code&gt; blocks with some description. The &lt;code&gt;When&lt;/code&gt; block is nothing but a way to organize tests in a logical fashion while each &lt;code&gt;Then&lt;/code&gt; serves as the actual test where the assertion happens. So you can have a test with just a &lt;code&gt;Then&lt;/code&gt; statement but then there&apos;s no point of using it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Next, lets add some functionality to the &lt;code&gt;GradeCalculator&lt;/code&gt; class.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  highlight-lines=&quot;4,19&quot;&gt;
          &lt;code slot=&quot;code&quot;&gt;class GradeCalculator {

  var totalMarks = 0

  fun getGrade(obtainedMarks: Int, totalMarks: Int): String {
    val percentage = getPercentage(obtainedMarks, totalMarks)
    return when {
      percentage &amp;gt;= 90 -&amp;gt; &amp;quot;A&amp;quot;
      percentage in 80..89 -&amp;gt; &amp;quot;B&amp;quot;
      percentage in 70..79 -&amp;gt; &amp;quot;C&amp;quot;
      percentage in 60..69 -&amp;gt; &amp;quot;D&amp;quot;
    else -&amp;gt; &amp;quot;F&amp;quot;
    }
  }

  private fun getPercentage(obtained: Int, total: Int): Int {
    return  (obtained / total.toFloat() * 100).roundToInt()
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Here I added a &lt;code&gt;totalMarks&lt;/code&gt; field which we mock in our test. This value is used in the &lt;code&gt;getPercentage&lt;/code&gt; method to calculate the percentage between total and obtained marks.&lt;/p&gt;
&lt;p&gt;Finally, the &lt;code&gt;getGrade&lt;/code&gt; calculates the grade by comparing the calculated percentage with different ranges.&lt;/p&gt;
&lt;p&gt;You can build this class in a TDD fashion by running the tests first and adding the functionality to make the failing tests pass one by one. I believe the end result would still be somewhat similar.&lt;/p&gt;
&lt;p&gt;In the end, lets add some assertions to test the specifications we just wrote. The final implementation would look something like this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;package com.zuhaibahmad.bddtestingtutorial

import io.kotlintest.shouldBe
import io.kotlintest.specs.BehaviorSpec
import io.mockk.every
import io.mockk.spyk

class GradeCalculatorSpec : BehaviorSpec({

  Given(&amp;quot;a grade calculator&amp;quot;) {
    val calculator = spyk(GradeCalculator())

    every { calculator.totalMarks } returns 100
    val total = calculator.totalMarks

    When(&amp;quot;obtained marks are 90 or above&amp;quot;) {
      val grade = calculator.getGrade(93, total)

      Then(&amp;quot;grade is A&amp;quot;) {
        grade.shouldBe(&amp;quot;A&amp;quot;)
      }
    }
    When(&amp;quot;obtained marks are between 80 and 89&amp;quot;) {
      val grade = calculator.getGrade(88, total)

      Then(&amp;quot;grade is B&amp;quot;) {
        grade.shouldBe(&amp;quot;B&amp;quot;)
      }
    }
    When(&amp;quot;obtained marks are between 70 and 79&amp;quot;) {
      val grade = calculator.getGrade(78, total)

      Then(&amp;quot;grade is C&amp;quot;) {
        grade.shouldBe(&amp;quot;C&amp;quot;)
      }
    }
    When(&amp;quot;obtained marks are between 60 and 69&amp;quot;) {
      val grade = calculator.getGrade(68, total)

      Then(&amp;quot;grade is D&amp;quot;) {
        grade.shouldBe(&amp;quot;D&amp;quot;)
      }
    }
    When(&amp;quot;obtained marks are below 60&amp;quot;) {
      val grade = calculator.getGrade(59, total)

      Then(&amp;quot;grade is F&amp;quot;) {
        grade.shouldBe(&amp;quot;F&amp;quot;)
      }
    }
  }
})&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;I discussed above that JUnit tests lack contextual information and proper grouping of co-related tests. You can see how every test in the spec has a hierarchy which can be used to compose complex cases.&lt;/p&gt;
&lt;p&gt;Furthermore, for this particular style of testing, kotlintest provides an additional &lt;code&gt;And&lt;/code&gt; block to allow you to create even more complex tests without loosing contextual information. For example, you may want to construct a test like this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;When(&amp;quot;user list is fetched from API&amp;quot;) {
  And(&amp;quot;the internet is NOT available&amp;quot;){
    Then(&amp;quot;Test something&amp;quot;){
      // Some assertions
    }
  }
  And(&amp;quot;the internet is available&amp;quot;){
    Then(&amp;quot;Test something&amp;quot;){
      // Some assertions
    }
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Finally, not just the tests are well organized, the test results in Android Studio have that nested style too. Here&apos;s the result of our tests in the IDE:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/652f7639b0677492bfac57c06e115aca/11b93/spec-test-results.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 52.5%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAALCAYAAAB/Ca1DAAAACXBIWXMAABYlAAAWJQFJUiTwAAABl0lEQVR42m2T6W6DMBCEU7USibls7GCOQIBA2kqt1Pd/uenuGmir9MdmNwefZ3acg5sH9MsH+vkT4/sXsnOHp6PGi7J4iS2eVbHOTt5LJS5U/FPPpwJPUY5DVlYolwGmnqhGFM0E285rX2AvC3Q1IC87OuyCzLU098j9FTkdrmnWfkBa1DjGGodIaei2gb9PBJnlx44gDDQMvdzpoSsSU8lDsfFIbYNTahGpDFGcr6VxTAwO/BKgFfx8E6XaE7QjUHUVZbEukRQVdU/AUoAqdwI4pYX0rQSoMgc7dvDTAuNHsc5WTRVmthmsdlLpOhtaBR+wqduBfIppa5S3WSC8M0dWTT2IyoRsSpHlZLXNinlmMQ8KeR/22gpQr8AtDF56WoT9sdXUNfsef6zrR2DRtziPt92m29Ptd2UMC6A6rIFSV3kp6T5avjSkkJKtJrHJV4Z3pP1meVNZS0gcEH92yuw/oZDs841Cmd8EyFfGda/Sufi+SVH6rJr3F6n8Txh/gPyF0lZU8j+FrbGysKPzakmH/g/kN/AbTtYMbgICncoAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Spec Test Result&quot;
        title=&quot;&quot;
        src=&quot;/static/652f7639b0677492bfac57c06e115aca/6af66/spec-test-results.png&quot;
        srcset=&quot;/static/652f7639b0677492bfac57c06e115aca/69538/spec-test-results.png 160w,
/static/652f7639b0677492bfac57c06e115aca/72799/spec-test-results.png 320w,
/static/652f7639b0677492bfac57c06e115aca/6af66/spec-test-results.png 640w,
/static/652f7639b0677492bfac57c06e115aca/d9199/spec-test-results.png 960w,
/static/652f7639b0677492bfac57c06e115aca/11b93/spec-test-results.png 1124w&quot;
        sizes=&quot;(max-width: 640px) 100vw, 640px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;You can find complete source code for this post &lt;a href=&quot;https://github.com/xuhaibahmad/Spec-Testing-Demo&quot;&gt;here&lt;/a&gt;{:target=&quot;_blank&quot;}&lt;/p&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Inter-Process Communication in Android - Lessons & Learnings]]></title><description><![CDATA[Recently, I was tasked with building a solution for OTA (Over-the-Air) updates for one of our products at SodaLabs{:target="_blank"}. Now…]]></description><link>https://www.zuhaibahmad.com/android-ipc/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/android-ipc/</guid><pubDate>Sun, 28 Apr 2019 11:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Recently, I was tasked with building a solution for OTA (Over-the-Air) updates for one of our products at &lt;a href=&quot;https://sodalabs.co/&quot;&gt;SodaLabs&lt;/a&gt;{:target=&quot;_blank&quot;}. Now for those of you who are not familiar with the concept, here&apos;s a definition by &lt;a href=&quot;https://searchmobilecomputing.techtarget.com/definition/OTA-update-over-the-air-update&quot;&gt;TechTarget&lt;/a&gt;{:target=&quot;_blank&quot;}:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An over-the-air update is the wireless delivery of new software or data to mobile devices. Wireless carriers and original equipment manufacturers (OEMs) typically use over-the-air (OTA) updates to deploy firmware and configure phones for use on their networks. The initialization of a newly purchased phone, for example, requires an over-the-air update. With the rise of smartphones, tablets and internet of things (IoT) devices, carriers and manufacturers have also turned to over-the-air updates for deploying new operating systems (OSs) to these devices.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Basically, the updates you receive for your Android, iOS, Windows, Mac or Linux devices are distributed through the mechanism known as OTA programming.&lt;/p&gt;
&lt;h2&gt;Going the Sandbox Way&lt;/h2&gt;
&lt;p&gt;We had a couple of choices around how to build this OTA client, but in the end we decided to build it as a completely separate project. The reason was that both the main product and OTA client were equally complex solutions and we didn&apos;t want to make it more complex by adding the OTA as part of the main project.&lt;/p&gt;
&lt;p&gt;The OTA would essentially be just a service that runs as a background process and communicate with the main application (the product whose updates this OTA was supposed to manage) via AIDL i.e. the IPC mechanism used by Android.&lt;/p&gt;
&lt;p&gt;This was the first time I was going to be working with IPC in general. So unsurprisingly, there was a lot of frustration, learning and fixing through trial and error involved.&lt;/p&gt;
&lt;p&gt;My goal in this post is to write about the basics and share some insights from my experience so that you can avoid the confusions that I had to face.&lt;/p&gt;
&lt;h2&gt;Inter-Process Communication&lt;/h2&gt;
&lt;p&gt;Let&apos;s start by understand what Inter-Process communication is. Inter-Process Communication or IPC in short, is a mechanism that allows multiple independent processes to communicate and exchange data.&lt;/p&gt;
&lt;p&gt;This communication is often achieved through the use of some shared interfaces defined through Interface Description Language (IDL). What it does is basically establish a set of rules that all the interested processes follow. These shared interfaces serves as a common language that all the parties use to communicate with each other.&lt;/p&gt;
&lt;h2&gt;How IPC Works In Android&lt;/h2&gt;
&lt;p&gt;All the applications in Android runs in its own process in a sandbox environment. Which means that each application is independent of others. This isolation helps improve the performance of overall system and in achieving better security through different levels of permissions and access privileges.&lt;/p&gt;
&lt;p&gt;Android has its own version of IDL called Android Interface Definition Language (AIDL), which has very much a Java-esque syntax. You basically start by defining an interface (or multiple based on your use case) and then implement the interface in your application and in the client application that wishes to communicate our app.&lt;/p&gt;
&lt;h2&gt;Understanding The Architecture&lt;/h2&gt;
&lt;p&gt;One important thing to understand is that IPC communication in Android is also client-server based. Which means that one application has to act as the server while others can interact with it using the interface it exposes.&lt;/p&gt;
&lt;p&gt;Generally that Server application is the one you are writing and which you wish to be controlled from outside sources. Following are the components and steps involved:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;An AIDL interface:&lt;/strong&gt; You define an interface in your server application containing the API that you want to expose to your clients.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Implementation of AIDL interface:&lt;/strong&gt; Rebuilding your project will generate a java interface from your AIDL file, which you can then use to write a concrete implementation of it.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Expose the interface to the clients:&lt;/strong&gt; Now that you have a concrete implementation of your AIDL interface, it is time to expose it to your clients by binding it with a Service. This Service can be invoked by external applications and then used a communication channel.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Exchanging data via IPC:&lt;/strong&gt; On Android, one process cannot normally access the memory of another process. So to talk, they need to decompose their objects into primitives that the operating system can understand, and marshall the objects across that boundary for you. This means that you can only exchange primitive type data through IPC in the most basic from.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Writing the parcelable implementations:&lt;/strong&gt; Since in most real-world applications you cannot live off just the primitives. Android also provides a mechanism to exchange your Objects through IPC with the use of Parcelables.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the 5 main steps required to implement the IPC capabilities in an application. However, admittedly they are not simple and bound to give you a lot of trouble. Let&apos;s look at the implementation now.&lt;/p&gt;
&lt;h2&gt;Off To The Code&lt;/h2&gt;
&lt;p&gt;Let&apos;s build a very basic application that let&apos;s you perform addition. To demonstrate the use of data exchange with both primitives and objects, instead of returning a simple integer value for result, we will return a made-up &lt;code&gt;Result&lt;/code&gt; object.&lt;/p&gt;
&lt;p&gt;Start by creating a simple project with two modules, one serving as a Client while the other as the Server. You can find the link to the complete code at the end of post.&lt;/p&gt;
&lt;h3&gt;The AIDL Interface&lt;/h3&gt;
&lt;p&gt;Now that you have a basic setup, let&apos;s first define our very simple AIDL interface in which we want to expose a single method called &lt;code&gt;performAddition&lt;/code&gt; which takes 2 integer values and returns a made-up &lt;code&gt;Result&lt;/code&gt; object.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// IAddition.aidl
package com.zuhaibahmad.aidldemo;

import com.zuhaibahmad.aidldemo.Result;
// Declare any non-default types here with import statements

interface IAddition {
  Result performAddition(int numOne, int numTwo);
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now as I mentioned above, since we are using a custom object, we need to define a simple AIDL implementation for it as well.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// Result.aidl
package com.zuhaibahmad.aidldemo;

parcelable Result;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Build the project and if no errors occur, then you should be able to find a java implementation of of your AIDL interface.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Once successful, make sure to copy both of your AIDL files to the client project as well.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Implementation Of The AIDL Interface&lt;/h3&gt;
&lt;p&gt;Next step is to provide concrete implementation of the AIDL interfaces. For the &lt;code&gt;Result&lt;/code&gt; object, all you need is a &lt;code&gt;parcelable&lt;/code&gt; POJO for it. However! You need to write it the old school java way.&lt;/p&gt;
&lt;p&gt;I know you love Kotlin and must be thinking of using &lt;code&gt;parcelize&lt;/code&gt; or at least take advantage of the simpler Kotlin code for data classes, but for some reason Kotlin based parcelables don&apos;t work at the time of this writing.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://media.giphy.com/media/26u4cIC1w7AKK9LJ6/giphy.gif#center&quot; alt=&quot;Oh well!&quot;&gt;&lt;/p&gt;
&lt;p&gt;So here&apos;s your implementation for &lt;code&gt;Result&lt;/code&gt; class in &lt;em&gt;pure&lt;/em&gt; java style:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Make sure to copy this class into client&apos;s source code as well since it is needed by client to interpret the &lt;code&gt;Result&lt;/code&gt; AIDL interface.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;deckgo-highlight-code language=&quot;java&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;package com.zuhaibahmad.aidldemo;

import android.os.Parcel;
import android.os.Parcelable;

public class Result implements Parcelable {
private int numOne;
private int numTwo;
private int result;

  public Result() {}

  public Result(int numOne, int numTwo, int result) {
    this.numOne = numOne;
    this.numTwo = numTwo;
    this.result = result;
  }

  public int getNumOne() {
    return numOne;
  }

  public void setNumOne(int numOne) {
    this.numOne = numOne;
  }

  public int getNumTwo() {
    return numTwo;
  }

  public void setNumTwo(int numTwo) {
    this.numTwo = numTwo;
  }

  public int getResult() {
    return result;
  }

  public void setResult(int numResult) {
    this.result = numResult;
  }

  @Override
  public String toString() {
    return numOne + &amp;quot; + &amp;quot; + numTwo + &amp;quot; = &amp;quot; + result;
  }

    @Override
  public int describeContents() {
    return 0;
  }

  @Override
  public void writeToParcel(Parcel parcel, int i) {
    parcel.writeInt(numOne);
    parcel.writeInt(numTwo);
    parcel.writeInt(result);
  }

  public static final Parcelable.Creator&amp;lt;Result&amp;gt; CREATOR = new Parcelable.Creator&amp;lt;Result&amp;gt;(){

    @Override
    public Result createFromParcel(Parcel parcel) {
      Result res=new Result();
      res.numOne = parcel.readInt();
      res.numTwo = parcel.readInt();
      res.result = parcel.readInt();
      return res;
    }

    @Override
    public Result[] newArray(int size) {
      return new Result[size];
    }
  };
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;img src=&quot;https://media.giphy.com/media/84BjZMVEX3aRG/giphy.gif#center&quot; alt=&quot;My eyes bleed&quot;&gt;&lt;/p&gt;
&lt;p&gt;Anyways, let&apos;s now provide the implementation for our &lt;code&gt;IAddition&lt;/code&gt; AIDL interface in the server application:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;package com.zuhaibahmad.aidldemo

import android.app.Service
import android.content.Intent
import android.os.IBinder
import android.util.Log

class AdditionService : Service() {

  override fun onBind(intent: Intent?): IBinder? {
    Log.e(&amp;quot;server&amp;quot;, &amp;quot;Binding Service&amp;quot;)
    return object : IAddition.Stub() {
      override fun performAddition(numOne: Int, numTwo: Int): Result {
        return Result(numOne, numTwo, numOne + numTwo)
      }
    }
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;While we are at it, let&apos;s also register this service in our &lt;code&gt;manifest&lt;/code&gt; file. With that our server application will be ready:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;xml&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;lt;service
  android:name=&amp;quot;.AdditionService&amp;quot;
  android:enabled=&amp;quot;true&amp;quot;
  android:exported=&amp;quot;true&amp;quot; &amp;gt;
  &amp;lt;intent-filter&amp;gt;
    &amp;lt;action
      android:name=&amp;quot;com.zuhaibahmad.aidldemo.AdditionService&amp;quot;
    /&amp;gt;
  &amp;lt;/intent-filter&amp;gt;
&amp;lt;/service&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Hooking The Service To The Client&lt;/h3&gt;
&lt;p&gt;Last step is to bind the &lt;code&gt;AdditionService&lt;/code&gt; in the server application to the client app and starting it remotely. I have a very basic activity with the following UI for client app:&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/65024c4b442944a637c2fd7822f8379c/d5b59/aidl_demo_ui.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 175.625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAjCAYAAACU9ioYAAAACXBIWXMAAAsTAAALEwEAmpwYAAAFSUlEQVR42q2Wa2wUVRTH7+5Ml7J039vddmdntjv76HRbhMgHv/NJE2OiYNKEhzZGEyg+IiCVKojtAqUFWl6FtrRFWFSIUMAHgvpBsKWUZ1tMMISYqLVKNLAYgczsHs+dfcy2VrMJTPLLPefcc/9z5859EYLPM9XVnkhVVVTw+fp4nu9HrnOcd4zzem97OO5vj8fzwOPhHqB9D+04x3G3kB+9Xu8lzP1CFMWWuXPnciTzRCKVbaVuN7jsTrnU5Qa72QJ2iwUcFis4zFbg3CXgLSnFmBWKrTYottnAiaXDaqUxuaS4GCKRSFtW0O8P9LoCfiiNlN8XqiIJV3koUVpRniyRwklboAzsIVHFKpaBNaCStAf9Sb6yIsHPrHzgCooQDodjWUEhKO7jXlkI/KplslT/OrjfeAl8K5eC47Ua0L3wHJDFzwJ5cR6Qmvkp0C5asgiCda/CE01rFXFpDYRDIU3QFwrtn1G3DPwdW+SSrQ1QuLEegruawd3aCPz2DeBua4QCjJHoKiDr67BMs24FkI2rFWbFEoiUS6cnCJrqauGxnjZ5TucWsDa/A3O6toB/RxRmdbTA7M7NYGxCgYY3gTQuz6Kj5aY6Rb/8ZZhZWTWYFQxGKo8ya9qBbDuukJZDQFqPANl8GMjWT1Ilpe0o1vdNhMZ2n1SY9/ZAOBA4pf3l2Y+f1UdPgK73ekLfcRV0iL5zGMthtaQQjP2LPZeB9P6gMBs+h6C/7GBWsHzmrDP6RuxV94hCdp/HxKH82D0IpGdUYdYfo4Ifap9cUYWC+AndoyhIky/kB315zzUUPA6BshzBgFR5Rh99hIKiFPmWUQVHHo2gv1w6xESPPZSg6PN9lDMPpQMsDiwOsKxDQR0m50VakFUFBU1QCEkxNvpwgn4hR5APhWNMA45h14hMdmLSrqGpab8w0d85SNsoTOMxKvhxVjAUlmKx/qvQ/6csf/3zHfjml3iWXH9y3Vc/3cY2inJwYBhwb9QEJakiduPGDcBHTiQSkPvEb41D/PcxiI+Pwd0/bkEyqdWlc5WbN28CbriaYFiSYkfOj8CVOMj9v8ZhYPwunPvtL5Xvxu4AjWWgsYHxVN0A+lfuJpWjQ6Pg5bhDOT8lHGPX4ULfMyzrdgyCvh3HZ8f5FDuHJoIxHY6zfhfWbR+kbRT2/T7we705gsHwwWkbjoO+95ps7LoI9p6LoO8YgoJOWl4AU/clsPVeVm1d1yUoxTpx21k1h933vTJt4wnwTRbMzEN1srafS28AU09oBntn3NaPX6JNG5+X+y9BFOq4+P8rhNZj71MrZTQt6DmszcPJgnksO526feUIeh5SUFvLUwkG8C9H8S93D8ukfTCVmA90rLvxL6/vmywYOMCuxBOsuUkm0QY82Rrzg+Y2NynsWyvoX84RFMX97OpaPJzWyGTT25i0WgN9Y+talUydrqU+W0da1yps/TLgJ/RQ9H/Azn8aD/FqmSx+HsgiPMwXItSmLJyXimX8BfNSMWrXVCsMthW4nLUsCMI+lmUBTRkB04wZUOxwUF/FaberZHy/zwf0LpT2FdpWyN2+0OmdNn06MAaDzBQUgLGoCEx4WaI2pchsVqG2HhvbnU4w40WJ2qzBoNC2As9rR4DAC3tdLhc4nU65GG9SDuwdhdpT+XbsbY6v0BKvdQe0MeT5vTTBZrPds1qtMqJYLJYs1M+NZXyai23u07a4fWmCuDnWOulnmM1Jo9EIhYWFeYG5SdqGCuJFtDajx+JbLMiTmPCuwWDoxkE+gnyKnES+RE4jp9LQ2Gc0h+bSNiaT6SmqQbX+ATh62khbGKU+AAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Client App Screenshot&quot;
        title=&quot;&quot;
        src=&quot;/static/65024c4b442944a637c2fd7822f8379c/6af66/aidl_demo_ui.png&quot;
        srcset=&quot;/static/65024c4b442944a637c2fd7822f8379c/69538/aidl_demo_ui.png 160w,
/static/65024c4b442944a637c2fd7822f8379c/72799/aidl_demo_ui.png 320w,
/static/65024c4b442944a637c2fd7822f8379c/6af66/aidl_demo_ui.png 640w,
/static/65024c4b442944a637c2fd7822f8379c/d9199/aidl_demo_ui.png 960w,
/static/65024c4b442944a637c2fd7822f8379c/21b4d/aidl_demo_ui.png 1280w,
/static/65024c4b442944a637c2fd7822f8379c/d5b59/aidl_demo_ui.png 1370w&quot;
        sizes=&quot;(max-width: 640px) 100vw, 640px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;Let&apos;s implement the activity now:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;package com.zuhaibahmad.aidldemo

import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.Bundle
import android.os.IBinder
import android.support.v7.app.AppCompatActivity
import android.util.Log
import kotlinx.android.synthetic.main.activity_main.btEquals
import kotlinx.android.synthetic.main.activity_main.etNumOne
import kotlinx.android.synthetic.main.activity_main.etNumTwo
import kotlinx.android.synthetic.main.activity_main.tvResult

class MainActivity : AppCompatActivity(), ServiceConnection {

  private var isBound: Boolean = false
  private var iRemoteService: IAddition? = null

  // Called when the connection with the service is established
  override fun onServiceConnected(
    className: ComponentName,
    service: IBinder
  ) {
    // Following the example above for an AIDL interface,
    // this gets an instance of the IRemoteInterface,
    // which we can use to call on the service
    Log.e(&amp;quot;client&amp;quot;, &amp;quot;Service connected!&amp;quot;)
    iRemoteService = IAddition.Stub.asInterface(service)
    isBound = true
  }

  // Called when the connection with the service disconnects
  override fun onServiceDisconnected(className: ComponentName) {
    Log.e(&amp;quot;client&amp;quot;, &amp;quot;Service disconnected!&amp;quot;)
    iRemoteService = null
  }

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    bindService()

    btEquals.setOnClickListener {
      onPerformAddition()
    }
  }

  private fun onPerformAddition() {
    val numOne = etNumOne.text.toString().toInt()
    val numTwo = etNumTwo.text.toString().toInt()

    tvResult.text = if (isBound) {
      val result = iRemoteService?.performAddition(numOne, numTwo)
      result.toString()
    } else {
      &amp;quot;Service not bound!&amp;quot;
    }
  }

  override fun onDestroy() {
    unbindService()
    super.onDestroy()
  }

  private fun bindService() {
    Log.e(&amp;quot;client&amp;quot;, &amp;quot;Attempting to bind service&amp;quot;)
    val serviceIntent = Intent()
    serviceIntent.setClassName(PACKAGE_NAME, SERVICE_NAME)
    serviceIntent.action = ACTION_REMOTE_BIND
    bindService(serviceIntent, this, Context.BIND_AUTO_CREATE)
  }

  private fun unbindService() {
    if (isBound) {
      // Detach our existing connection.
      unbindService(this)
      isBound = false
    }
  }

  companion object {
    @JvmStatic
    val PACKAGE_NAME: String = &amp;quot;com.zuhaibahmad.aidldemo&amp;quot;

    @JvmStatic
    val SERVICE_NAME: String = &amp;quot;com.zuhaibahmad.aidldemo.AdditionService&amp;quot;

    @JvmStatic
    val ACTION_REMOTE_BIND = &amp;quot;$SERVICE_NAME-remote-bind&amp;quot;
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;If all goes well, you should be able to compute your additions through the server app using AIDL interfaces.&lt;/p&gt;
&lt;h2&gt;Things To Remember&lt;/h2&gt;
&lt;p&gt;In the end, as you may have realized that this mechanism is not very robust and prone to error. So here are few tips from what I learnt working with Android&apos;s IPC about the things that could easily go wrong and make you waste a lot of time.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Be &lt;strong&gt;very&lt;/strong&gt; careful about the intent that you use to trigger the service, a minor typo will lead to service never starting.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;On the same note, if you face a situation at the start where you are not getting the results, verify your service bindings first before diving into debugging. It could just be a faulty service binding and will save you a lot of effort.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remember that ONLY primitive types are allowed, so always make sure that you are not accidentally accepting or returning a non-primitive object.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;One of the biggest pain was with threading, I never realized that thread would affect IPC as well. Basically, calls made from the local process are executed in the same thread that is making the call. If this is your main UI thread, that thread continues to execute in the AIDL interface. So be very careful if you are using RxJava or any other async mechanism.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Remember, Kotlin classes does not work for parcelable implementations with AIDL. At least not at the time of this writing, and what&apos;s worse is that it does not give you any error.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Helpful Resources&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;This awesome post by &lt;a href=&quot;https://devarea.com/android-services-and-aidl/&quot;&gt;DevArea&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/li&gt;
&lt;li&gt;Detailed guide about IPC in Android on &lt;a href=&quot;https://developer.android.com/guide/components/aidl&quot;&gt;Official Android Resources&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/li&gt;
&lt;li&gt;Also, this &lt;a href=&quot;https://android.jlelse.eu/android-aidl-937daf89e685&quot;&gt;Medium Article&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;You can find complete source code for this post &lt;a href=&quot;https://github.com/xuhaibahmad/AIDL-Demo&quot;&gt;here&lt;/a&gt;{:target=&quot;_blank&quot;}&lt;/p&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Pythonic Imports in Kotlin]]></title><description><![CDATA[If you have been working with Kotlin for some time now, you might have encountered issues with name conflicts with Kotlin's reserved…]]></description><link>https://www.zuhaibahmad.com/kotlin-import-as/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/kotlin-import-as/</guid><pubDate>Sat, 01 Dec 2018 16:00:00 GMT</pubDate><content:encoded>&lt;p&gt;If you have been working with Kotlin for some time now, you might have encountered issues with name conflicts with Kotlin&apos;s reserved keywords. A common example is with the popular mocking library &lt;a href=&quot;https://site.mockito.org/&quot;&gt;Mockito&lt;/a&gt;, where the &lt;code&gt;when&lt;/code&gt; method which is now used by Kotlin for its switch-like construct. So you end up doing something like this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;`when`(messenger.getMessage()).thenReturn(&amp;quot;Hello World!&amp;quot;)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Another scenario is when you want to name the imported class or extension function something more meaningful and readable. Which can be acheived to some extent with the use of a &lt;code&gt;typealias&lt;/code&gt;. However, there&apos;s a better solution available right into the Kotlin standard library.&lt;/p&gt;
&lt;h2&gt;Pythonic Way of Handling Imports&lt;/h2&gt;
&lt;p&gt;One the most flexible things I find in &lt;code&gt;Python&lt;/code&gt; is the ability to name the imported classes almost anything you want, just like a variable. So you would simply import a class with a name that suits your style e.g.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;python&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import matplotlib.pyplot as plt&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now, you can use &lt;code&gt;plt&lt;/code&gt; as a variable throughout your script.&lt;/p&gt;
&lt;h2&gt;Kotlin&apos;s Import-As Alias&lt;/h2&gt;
&lt;p&gt;I wished to have this for Android development after I ran into some ambiguity issues with a recent project. Upon some searching, I was surprized to find that Kotlin had this feature all along. Just like Python, you can simply add imports with an &lt;code&gt;as&lt;/code&gt; keyword.&lt;/p&gt;
&lt;p&gt;Let&apos;s look at how our Mockito problem is resolved now:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;import org.mockito.Mockito.`when` as whenever&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;You can now use &lt;code&gt;whenever&lt;/code&gt; in place of the less pleasant &lt;code&gt;&apos;when&apos;&lt;/code&gt; througout the class.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;whenever(messenger.getMessage()).thenReturn(&amp;quot;Hello World!&amp;quot;)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;So that was it. Another reason to love Kotlin (and Python as well)! ;)&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Parcelize: Boilerplate Free Parcelization With Kotlin]]></title><description><![CDATA[Serialization is the process of converting an object into a stream of bytes for ease of use for different I/O operations. Examples include…]]></description><link>https://www.zuhaibahmad.com/kotlin-parcelize/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/kotlin-parcelize/</guid><pubDate>Mon, 20 Aug 2018 12:25:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Serialization&quot;&gt;Serialization&lt;/a&gt; is the process of converting an object into a stream of bytes for ease of use for different I/O operations. Examples include writing to a database, memory or a file. In context of Android, the most common usecase is to pass an object around Activities and Fragments.&lt;/p&gt;
&lt;p&gt;The main idea is to be able to preserve the state of the object in the most primitive way and be able recreated it when needed. The reverse process is called Deserialization.&lt;/p&gt;
&lt;h2&gt;Java Serialization &amp;#x26; Android&lt;/h2&gt;
&lt;p&gt;The way our old boy Java used to handle serialization was very simple - You would only need to implement a &lt;a href=&quot;https://stackoverflow.com/questions/25850328/marker-interfaces-in-java&quot;&gt;marker interface&lt;/a&gt; i.e. &lt;code&gt;java.io.Serializable&lt;/code&gt; and objects of that class would be automagically serializable.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;public class Foo implements Serializable {
  private int data;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;blockquote&gt;
&lt;p&gt;This looks perfect, let&apos;s leave it here and use this awesome interface everywhere.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;But wait! This oversimplification comes at a big cost. The process involves reflection and creation of tonnes of helper objects behind the scenes. Which results in excessive garbage collection and poor performance.&lt;/p&gt;
&lt;p&gt;In order to solve this problem, Android came with its own mechanism - &lt;a href=&quot;https://developer.android.com/reference/android/os/Parcelable.html&quot;&gt;Parcelable&lt;/a&gt;. Although it is more robust, it sacrifices the simplicity of &lt;code&gt;Serialzable&lt;/code&gt; to acheive that. This results in more boilerplate code but which is completely reflection free.&lt;/p&gt;
&lt;p&gt;They key to acheiving that, is that we have to explicitly define the serialization process using the &lt;code&gt;writeToParcel&lt;/code&gt; method. Here&apos;s what your ordinary parcelable class with a single field looks like:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;public class Foo implements Parcelable {
  private int data;

  public int describeContents() {
    return 0;
  }

  public void writeToParcel(Parcel out, int flags) {
    out.writeInt(data);
  }

  public static final Parcelable.Creator&amp;lt;Foo&amp;gt; CREATOR
    = new Parcelable.Creator&amp;lt;Foo&amp;gt;() {

    public Foo createFromParcel(Parcel in) {
      return new Foo(in);
    }

    public Foo[] newArray(int size) {
      return new Foo[size];
    }
  };

  private Foo(Parcel in) {
    data = in.readInt();
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We have been using this for years now, but if you have been using something like &lt;a href=&quot;https://github.com/johncarl81/parceler&quot;&gt;Parceler&lt;/a&gt; or if you are used to minimal Kotlin code now, then your reaction would probably be something like:&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://media.giphy.com/media/pVAMI8QYM42n6/giphy.gif#center&quot; alt=&quot;Gross!&quot;&gt;&lt;/p&gt;
&lt;h2&gt;Kotlin To The Rescue (Again?!)&lt;/h2&gt;
&lt;p&gt;The release of Kotlin 1.1.4 added experimental support for generating Android Parcelable implementations using the &lt;code&gt;@Parcelize&lt;/code&gt; annotation. It brings back the simplicity of &lt;code&gt;Serializable&lt;/code&gt; but without reflection. It instead uses annotation processing to acheive that.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Since it is an experimental feature right now, you will need to enable the support for it by adding the following snippet to your &lt;code&gt;gradle&lt;/code&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;groovy&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;androidExtensions {
  experimental = true
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Also, add &lt;code&gt;apply plugin: ‘kotlin-android-extensions’&lt;/code&gt; to your gradle file if it isn&apos;t there already.&lt;/p&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;The process of making any class parcelable involves adding only two things - a &lt;code&gt;@Parcelize&lt;/code&gt; annotation and marking the class with &lt;code&gt;Parcelable&lt;/code&gt; implementation.&lt;/p&gt;
&lt;p&gt;Here&apos;s what our &lt;code&gt;Foo&lt;/code&gt; class would look like:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;@Parcelize
data class Foo (val data: Int) : Parcelable&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And that was it! When combined with data classes, we ended up with even simpler code than Java&apos;s &lt;code&gt;Serializable&lt;/code&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Why Reactive Should Be Default for Mobile]]></title><description><![CDATA[In the early days of software, the way code was written was pretty straight forward. You would start at a single entry point in the program…]]></description><link>https://www.zuhaibahmad.com/rx-blog/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/rx-blog/</guid><pubDate>Sat, 04 Aug 2018 01:10:00 GMT</pubDate><content:encoded>&lt;p&gt;In the early days of software, the way code was written was pretty straight forward. You would start at a single entry point in the program (typically a &lt;strong&gt;main&lt;/strong&gt; function), execute the instructions sequentially and end the program gracefully. It was simple and easy to conceptualize.&lt;/p&gt;
&lt;p&gt;Due to the linear control flow, it was not only easier to manage the &lt;a href=&quot;https://en.wikipedia.org/wiki/State_(computer_science)&quot;&gt;state&lt;/a&gt; of the program but also to test and debug it because all the possible outcomes were predictable. The success of this imperative approach contributed to the way programming mindset established in the years to come. Today, most of us like to think in imperative fashion regardless of the platform or technology we work with.&lt;/p&gt;
&lt;h2&gt;Imperative Approach Fails With Modern Systems&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;“All modern systems are asynchronous.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This may seem misleading at first but if you think about it, almost every system today requires us to code in non-blocking fashion. What I mean by non-blocking is that, you need to eliminate waiting periods from the system as much as you can. For example, it’s not ideal to display a loading indicator to the user while a network request is being processed. Although many still follow this pattern but from user experience standpoint, this is a recipe for disaster.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“Nobody likes to wait and they’ll leave if they are not served instantly.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The situation worsens with mobile platforms where the OS itself is a source of a countless external changes. You could just flip your device and it’ll break your app unless you have added 3x more code than required just to handle a rotation scenario. What is surprising though, is that almost all of these popular mobile platforms were not designed to be reactive despite them being asynchronous by nature.&lt;/p&gt;
&lt;h2&gt;Reactive Programming&lt;/h2&gt;
&lt;p&gt;Hmmm… So what exactly is Reactive Programming? Let’s take some help from Wikipedia:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;“In computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change.”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&quot;https://media.giphy.com/media/SqmkZ5IdwzTP2/giphy.gif#center&quot; alt=&quot;Wut?&quot;&gt;&lt;/p&gt;
&lt;p&gt;Never mind! So, consider you have a program in which we have a Button and Label. They are defined as classes, like any other thing in an object oriented system. All this program does is updates the Label’s text to “Clicked!” when the Button is clicked. So we can say that the Button can somehow affect the text inside the Label, right?&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 424px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/cc4a609faf317151e508ef8dcb0af3c5/1cfa9/relation.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABbklEQVR42n2RWU/CQBSFB9EHE6Mh8a/xe/gvJqhviqiIiQTKUovQshQoW9n3fVEMtB5nBoIPGm9ycs/L+ebeOySUbSOYaXEJatN80yd4jGQUQguAhexr761PkibGKlOaaRq7HCJaF95oHiTdXiHZfOdKNJZmYQwImcZv4I+3RrSOWJwAycbS4DkqtfNJoT0QCoFSm0Nmqs5MbfgFf6rGgXa73UrbAdfWM+hRUG295keAUl8Ycn2OeHWGVOsDoWwHxBstwHkv4PJB4CuzCX1KJUmDxzab7Yz2U66tP6E698l6vDQFXuSyeeHy4cYvU+AKYQaMVSZ8VDHfQ7Q8MrP9DXwJ/d+VA2pd1IYGpOLACOc6rNNzLRCktyS5gQkGyfUNqN21qS9AH+hzoMPhONitaSFbz+pQKo3EyhLI9jYGy2e6a7BTRctjkDshCVcgwXXrV0yvlIfTHfjjl8l+witPWHymp6IZY5eDO5TGtSeMbxgfU3wj+hPeAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;OO Relation&quot;
        title=&quot;&quot;
        src=&quot;/static/cc4a609faf317151e508ef8dcb0af3c5/1cfa9/relation.png&quot;
        srcset=&quot;/static/cc4a609faf317151e508ef8dcb0af3c5/69538/relation.png 160w,
/static/cc4a609faf317151e508ef8dcb0af3c5/72799/relation.png 320w,
/static/cc4a609faf317151e508ef8dcb0af3c5/1cfa9/relation.png 424w&quot;
        sizes=&quot;(max-width: 424px) 100vw, 424px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;So the case is simple, Button can be clicked and Label can be updated, both can be defined as a function in their respective classes. Now, here’s a question! If you were to code this interaction, where would you put the logic for updating the label when the button is clicked? Is it inside the Button itself or the Label?&lt;/p&gt;
&lt;p&gt;Let’s say you go with the button, which btw is how we normally do it, then in this case you’ll need a reference of Label inside your button which you can update whenever the button is clicked. This essentially is Proactive way of programming. We basically delegated all the responsibility of state-management to the Button and let the Label stay passive i.e. don’t care about what is going on outside. Just follow the instructions when interacted.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 424px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/dcf124bc0d3e196e0204b8edfb1afec4/1cfa9/proactive_relation.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABYElEQVR42n2RS0/CQBSFBzFxo3FB+L38FxPUnSIq4oJAaWlApDzaUt7I+/1WDEw9zowEFxomOTl3Meebe+8Q2ewgarSFJL1lv1SneIwZGmEHgIPsz752PsUtNVmbsUyL7nKIWT0EEwWQXGeNTOtdKN1c2cUJIBnNv8Df2hmzumppCmSaKypyTHr3k0H7IAwC7W2BFFd9blujL4SzbwLo8XiOmDmEfmoBlI2OWhgDWmNJU40FXutzZNsfkM0uSDBRhPdewuWDJEbmHYa0WoYFT1wu1xnzUyG3m/s507Gst9XyjN+r0gtfCDfhFAOuoXBgsjYVraqFPhKVsW0OtgilqwdHjugN1RpRxEtDquS73Nm6loiyXZL80AaH5AcUem9jV5dgDwwOAuPlsVpbAWZ/S3ne6G3AV5WoTEDupAx8kbTQbVizg/ECvP7IP79M9sCrgKI+s1WxDN3l4JdzuA4o+AaBI1bJl6RXUgAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Proactive Relation&quot;
        title=&quot;&quot;
        src=&quot;/static/dcf124bc0d3e196e0204b8edfb1afec4/1cfa9/proactive_relation.png&quot;
        srcset=&quot;/static/dcf124bc0d3e196e0204b8edfb1afec4/69538/proactive_relation.png 160w,
/static/dcf124bc0d3e196e0204b8edfb1afec4/72799/proactive_relation.png 320w,
/static/dcf124bc0d3e196e0204b8edfb1afec4/1cfa9/proactive_relation.png 424w&quot;
        sizes=&quot;(max-width: 424px) 100vw, 424px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;The class &lt;code&gt;Button&lt;/code&gt; would look something like this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;class Button (val label: Label) {
  public fun onClick() {
    label.setText(&amp;quot;Clicked!&amp;quot;)
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The alternative to this approach is Reactive programming. We basically just invert the roles so that the state owner has the ability to change itself, letting the Button call for change and Label react to it.&lt;/p&gt;
&lt;p&gt;&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 424px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/ee6078132d96c152a959f423eee733dd/1cfa9/reactive_relation.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 32.49999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAHCAYAAAAIy204AAAACXBIWXMAAAsTAAALEwEAmpwYAAABX0lEQVR42n2RWU/CQBSFB/HJ6IuE38t/MUF9U0RFTCRQKLUVCoUCZd/3fVEMdDzOFIIPGm9ycu/L+ebeMySS7SCcaVsS9BZ9q0zxGM0kCCsANnKow2x/kg0pVp0xT8vc+xA1evAreZB0Zw2t9W4p2VzRwgQQMs3fwJ/ZHjW6UnEKaM2VafmY9O4ng/ZBGASJ+gIqV21OjdEXgqm6BXS5XEes2SztZl7HYb39mh8DicbSVBsLxGtzpNofiGS7IH6lAPe9gMsHwTqZbxjUdkCn03nK2k4OxxnrJ0znAbUSL82AF7VMLzwB3ARVBlxD5MBYdWqtKuX7UMpjmh1sEUhW/j05pDckY2RCLg5NMdflncW1RJhlSXJDCg7JDUzovQ2tLMEeGPwLlEtjqboCsv2tyf2Z3gY8KqU8AbkTNHhCSUu3wQT1y3m4vaE/fpkcgFc+UXpmUTGPuffBG0nj2ifiG3iHVsHSncwpAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Reactive Relation&quot;
        title=&quot;&quot;
        src=&quot;/static/ee6078132d96c152a959f423eee733dd/1cfa9/reactive_relation.png&quot;
        srcset=&quot;/static/ee6078132d96c152a959f423eee733dd/69538/reactive_relation.png 160w,
/static/ee6078132d96c152a959f423eee733dd/72799/reactive_relation.png 320w,
/static/ee6078132d96c152a959f423eee733dd/1cfa9/reactive_relation.png 424w&quot;
        sizes=&quot;(max-width: 424px) 100vw, 424px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;&lt;/p&gt;
&lt;p&gt;In this case, the &lt;code&gt;Button&lt;/code&gt; class would be similar to this (notice how there&apos;s absolutely no trace of label or any outside dependency):&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;class Button {

  interface ClickListener {
    fun doOnClick()
  }

  var listener: ClickListener? = null

  public fun onClick() {
    listener?.doOnClick()
  }

  public fun addOnClickListener(l: ClickListener) {
    listener = l
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;While the &lt;code&gt;Label&lt;/code&gt; will carry all the code for state its management along with list of all the registered clients.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;class Label (val buttons: MutableList&amp;lt;Button&amp;gt;) {

  private var text = &amp;quot;&amp;quot;

  init {
    buttons.forEach {
      it.addOnClickListener {
        this.setText(&amp;quot;Clicked!&amp;quot;)
      }
    }
  }

  private fun setText(text: String) {
    this.text = text
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;blockquote&gt;
&lt;p&gt;But why reactive? What changes with this approach?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;First, the &lt;a href=&quot;https://en.wikipedia.org/wiki/Inversion_of_control&quot;&gt;Inversion of Control&lt;/a&gt; i.e. Label class is now completely responsible of managing its own state and can easily hide away all the implementation details from outside world.&lt;/p&gt;
&lt;p&gt;Second but related, is that now we have only one source of change. What I mean by that is how previously we had to make &lt;code&gt;setText&lt;/code&gt; method public and all the clients could call it whenever they wanted, which means increased complexity. Now we have a hidden &lt;code&gt;setText&lt;/code&gt; method that is called from only one place i.e. inside &lt;code&gt;Button&lt;/code&gt;&apos;s &lt;code&gt;doOnClick&lt;/code&gt; callback and you can attach as many clients to it as you want using the &lt;code&gt;addOnClickListener&lt;/code&gt; callback. This leads to &lt;a href=&quot;https://en.wikipedia.org/wiki/Separation_of_concerns&quot;&gt;Separation of Concern&lt;/a&gt; and simplified codebase.&lt;/p&gt;
&lt;h2&gt;Reactive Extensions&lt;/h2&gt;
&lt;p&gt;As described in the previous section, &lt;a href=&quot;https://en.wikipedia.org/wiki/Callback_(computer_programming)&quot;&gt;Callbacks&lt;/a&gt; are key enablers of reactive programming. However, this way of doing things can lead to a very uncomfortable situation known as &lt;a href=&quot;https://stackoverflow.com/.../what-is-callback-hell-and-how-and-why-rx-solves-it&quot;&gt;Callback Hell&lt;/a&gt;. &lt;a href=&quot;reactivex.io/&quot;&gt;Reactive Extensions&lt;/a&gt; (abbreviated as RX) is a framework that helps address such issues.&lt;/p&gt;
&lt;p&gt;The basic building blocks of the library are &lt;strong&gt;Observable&lt;/strong&gt;, &lt;strong&gt;Observers&lt;/strong&gt; and &lt;strong&gt;Operators&lt;/strong&gt;. If you are familiar with the &lt;a href=&quot;https://en.wikipedia.org/wiki/Observer_pattern&quot;&gt;Observer Pattern&lt;/a&gt;, this is essentially the same pattern with some enhancements. You make things Observable and let some other things Observe these observables, during the process where Observable produces some data and the Observable receives it, you apply some Operators to mold the data in the form you like to have at the receiving end.&lt;/p&gt;
&lt;p&gt;To learn more about the framework, checkout this &lt;a href=&quot;https://www.youtube.com/watch?v=k3D0cWyNno4&amp;#x26;t=1s&quot;&gt;talk by Kaushik Gopal&lt;/a&gt; or the &lt;a href=&quot;https://gist.github.com/staltz/868e7e9bc2a7b8c1f754&quot;&gt;guide by André Staltz&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Mobile &amp;#x26; RX Movement&lt;/h2&gt;
&lt;p&gt;Now that we know about Reactive Programming and its helper framework. The question is how that solve problems associated with mobile platform that I mentioned at the start of this post?&lt;/p&gt;
&lt;p&gt;Instead of giving long explanations of my own, I’d rather direct you to this awesome &lt;a href=&quot;https://www.youtube.com/watch?v=0IKHxjkgop4&amp;#x26;t=2179s&quot;&gt;talk by Jake Wharton&lt;/a&gt; where he discusses these problems in context of Android and their solution with use of RxJava.&lt;/p&gt;
&lt;p&gt;The crux is basically don’t hate yourself, go async and let the world handle their own problems instead of making your code rely on outside changes in proactive fashion.&lt;/p&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;State management is hard, especially on mobile platforms. While ideally SDKs should themself be reactively designed, we shouldn’t let that be a source of unreliability in our applications. Thanks to RX movement, we have reactive extensions port for almost every well known language. In context of Android specifically, the rise of Kotlin has provided a great opportunity to finally start writing our applications in functional and reactive manner.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Image Source - &lt;a href=&quot;http://reactivex.io/&quot;&gt;ReactiveX&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;br /&gt;
For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[Kotlin: Java on steroids or something more?]]></title><description><![CDATA[The first time I heard about Kotlin was back in 2015 and my initial reaction was like "Meh! Just another heck into Android ecosystem that…]]></description><link>https://www.zuhaibahmad.com/kotlin-intro/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/kotlin-intro/</guid><pubDate>Sat, 28 Oct 2017 01:10:00 GMT</pubDate><content:encoded>&lt;p&gt;The first time I heard about Kotlin was back in 2015 and my initial reaction was like &lt;em&gt;&quot;Meh! Just another heck into Android ecosystem that would lead to disasters. I&apos;ll pass!&quot;.&lt;/em&gt; I waited and waited for the hype to die down -- but it never did.&lt;/p&gt;
&lt;p&gt;The popularity of Kotlin kept on increasing. There was something about Kotlin, there was something that&apos;s lacking in Java, I just didn&apos;t knew what it was and being a Java fanboy I never bothered investigating. Whatever the case was, everyone was going crazy over it. So finally the prayers were answered and Google made &lt;a href=&quot;https://developer.android.com/kotlin/index.html&quot;&gt;Kotlin an Official language for Android&lt;/a&gt;{:target=&quot;_blank&quot;} at the &lt;a href=&quot;https://events.google.com/io/&quot;&gt;Google I/O &apos;17&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;
&lt;h2&gt;Background&lt;/h2&gt;
&lt;p&gt;The reason for my immediate disapproval was because of the huge number of half-baked platforms and tools emerging for Android during that period. Every single one of them was following the same pattern. They all started out with a bang, did some impressive demos, showed great promise, made it look like everything is going to change, eventually hit technical barriers and then disappeared forever or almost died.&lt;/p&gt;
&lt;p&gt;They were hyped not because there was something groundbreaking about them but because there were actual problems with Java and people were desperately looking for alternatives. Anyone coming from Python, Swift or Javascript background could see the huge gap between Java and other modern programming languages.&lt;/p&gt;
&lt;p&gt;While there were &lt;em&gt;lambdas&lt;/em&gt;, &lt;em&gt;optional types&lt;/em&gt;, easier &lt;em&gt;asynchronous processing&lt;/em&gt; mechanisms and loads of other useful features in others; Java developers were still fighting &lt;em&gt;Null Pointer Exceptions&lt;/em&gt; and writing tons of boilerplate crap everyday.&lt;/p&gt;
&lt;h2&gt;Kotlin To The Rescue&lt;/h2&gt;
&lt;p&gt;Right after the acceptance from Google and built-in support for it in Android Studio 3, I started taking interest in it. Boy I was wrong about Kotlin! Just this single &lt;a href=&quot;https://www.youtube.com/watch?v=X1RVYt2QKQE&quot;&gt;demo by Hadi Hariri&lt;/a&gt;{:target=&quot;_blank&quot;} done at the Google I/O was enough to convert me. Learning curve is lean for anyone familiar with Java so it took roughly 2 days to grasp the core concepts and starting to write first production application.&lt;/p&gt;
&lt;p&gt;Now I was having a good time with it (already a fan btw!), the codebase was very concise, boilerplate free and readable - in fact pretty much self-explanatory. However, still I couldn&apos;t come up with an expression to explain that magic. Sure there were thousands of technical arguments in its favor but it was not something you&apos;d tell your manager when trying to convince them to use it. Then recently while watching &lt;a href=&quot;https://www.youtube.com/watch?v=gT6il5fJyAs&quot;&gt;this talk on RxJS&lt;/a&gt;{:target=&quot;_blank&quot;} it clicked!&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Programming should be more about &quot;what&quot;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;One of the reasons why Python is so popular in scientific and research domains is because it is doing exactly that. It enables them to focus on solving problems instead of tangling into platform specific low level details.&lt;/p&gt;
&lt;p&gt;On the other hand we are still focusing too much on &apos;How&apos; rather than &apos;What&apos; to do. A developer should be concerned with completing the task at hand with few lines of code instead of fighting the issues and barriers put in their path by the platform itself.&lt;/p&gt;
&lt;p&gt;Alright, enough ranting about Java! Let&apos;s take a look at what Kotlin has to offer that makes it different.&lt;/p&gt;
&lt;h2&gt;Bringing Good Of All Worlds&lt;/h2&gt;
&lt;p&gt;After having a taste of Python, Javascript and a little bit of Swift, it was easy to identify the source of inspiration for many features (Yes, I know Kotlin is older than Swift and they didn&apos;t copy). However, even after using it for a few months, I believe there are still a lot of undiscovered bits that are yet to be explored. So for brevity&apos;s sake, I&apos;ll be focusing only on the major features.&lt;/p&gt;
&lt;h3&gt;Type Safety&lt;/h3&gt;
&lt;p&gt;The selling point for &lt;a href=&quot;https://www.jetbrains.com&quot;&gt;JetBrains&lt;/a&gt;{:target=&quot;_blank&quot;} has been &lt;strong&gt;&quot;NPE Free Language&quot;&lt;/strong&gt; for the most part and why wouldn&apos;t it be? We&apos;re all familiar with null pointer exception jokes made on Java developers.&lt;/p&gt;
&lt;p&gt;Specially on Android it is quite annoying when the entire application just blows up in your face due to a sudden NPE and then as a developer you&apos;ll have to spend a lot of time going back to the same state where you were before.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;.. and GOD Help you if they show up in the production code.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In Kotlin, the syntax is different for references that can hold &lt;code&gt;null&lt;/code&gt; and those that can not. For example, a regular variable of type &lt;code&gt;String&lt;/code&gt; can not hold &lt;code&gt;null&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;var a: String = &amp;quot;abc&amp;quot;
a = null // This leads to compile time error&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;The syntax to allow &lt;code&gt;null&lt;/code&gt;s is to add a trailing question mark after the type declaration e.g. &lt;code&gt;String?&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;var b: String? = &amp;quot;abc&amp;quot;
b = null // Acceptable&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now, if you call a method or access a property on &lt;code&gt;a&lt;/code&gt;, it&apos;s guaranteed not to cause an NPE, so you can safely say:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;val l = a.length&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;But if you want to access the same property on &lt;code&gt;b&lt;/code&gt;, that would not be safe, and the compiler reports an error:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;val l = b.length // error: variable &amp;#39;b&amp;#39; can&amp;#39;t be null&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;It doesn&apos;t stop at this, type safe calls allows you to eliminate &lt;code&gt;if (someValue != null)&lt;/code&gt; blocks for nullable types with simple &lt;code&gt;?.&lt;/code&gt; operator which essentially means &lt;em&gt;execute this only when the value is not &lt;code&gt;null&lt;/code&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;val listWithNulls: List&amp;lt;String?&amp;gt; = listOf(&amp;quot;A&amp;quot;, null)
for (item in listWithNulls) {
  item?.let { println(it) } // prints A and ignores null
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;You can also apply an alternate logic for &lt;code&gt;null&lt;/code&gt; case using &lt;strong&gt;elvis&lt;/strong&gt; operator which is equivalent to the &lt;code&gt;else&lt;/code&gt; block that follows the &lt;em&gt;if not null&lt;/em&gt; block. The syntax for it is &lt;code&gt;?:&lt;/code&gt;, so your conditional code will look something like this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;val l = b?.length ?: -1&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This is just an overview, it&apos;ll take at least an entire post to cover all of the type system. You can read more about it on the &lt;a href=&quot;https://kotlinlang.org/docs/&quot;&gt;Kotlin&apos;s official documentation&lt;/a&gt;{:target=&quot;_blank&quot;}&lt;/p&gt;
&lt;h3&gt;Data Classes&lt;/h3&gt;
&lt;p&gt;This one is my personal favorite so far. How much code do you have to write for a data class? And how much more do you have to add if you need to add &lt;code&gt;equals()&lt;/code&gt;, &lt;code&gt;hashCode()&lt;/code&gt;, &lt;code&gt;clone()&lt;/code&gt; and &lt;code&gt;toString()&lt;/code&gt; functionality? This is your average data class declaring fields and providing that additional functionality along with getters and setters:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;java&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;public class Customer {
  private String name;
  private String email;
  private String company;

  public Customer(String name) {
    this(name, &amp;quot;&amp;quot;, &amp;quot;&amp;quot;);
  }

  public Customer(String name, String email) {
    this(name, email, &amp;quot;&amp;quot;);
  }

  public Customer(String name, String email, String company) {
    this.name = name;
    this.email = email;
    this.company = company;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }

  public String getEmail() {
    return email;
  }

  public void setEmail(String email) {
    this.email = email;
  }

  public String getCompany() {
    return company;
  }

  public void setCompany(String company) {
    this.company = company;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) return true;
    if (o == null || getClass() != o.getClass()) return false;

    Customer c = (Customer) o;

    if (name != null ? !name.equals(c.name) : c.name != null)
      return false;
    if (email != null ? !email.equals(c.email) : c.email != null)
      return false;
    return company != null
      ? company.equals(c.company)
      : c.company == null;
  }

  @Override
  public int hashCode() {
    int result = name != null ? name.hashCode() : 0;
    result = 31 * result + (email != null ? email.hashCode() : 0);
    result = 31 * result + (company != null ? company.hashCode() : 0);
    return result;
  }

  @Override
  public String toString() {
    return &amp;quot;Customer{&amp;quot; +
    &amp;quot;name=&amp;#39;&amp;quot; + name + &amp;#39;\&amp;#39;&amp;#39; +
    &amp;quot;, email=&amp;#39;&amp;quot; + email + &amp;#39;\&amp;#39;&amp;#39; +
    &amp;quot;, company=&amp;#39;&amp;quot; + company + &amp;#39;\&amp;#39;&amp;#39; +
    &amp;#39;}&amp;#39;;
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;strong&gt;That&apos;s nearly 80 lines for a simple POJO!&lt;/strong&gt; Even if the IDE generates them for us, it is every difficult to maintain in the longer run. Now let&apos;s look at what the Kotlin equivalent of this would look like:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;data class Customer(var name: String, var email: String = &amp;quot;&amp;quot;, var company: String = &amp;quot;&amp;quot;)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Yes, that&apos;s it! A single line that provide you a fully functional data object with &lt;code&gt;equals()&lt;/code&gt;, &lt;code&gt;hashCode()&lt;/code&gt;, &lt;code&gt;clone()&lt;/code&gt; and &lt;code&gt;toString()&lt;/code&gt; functionality baked right into it. So now your entire package of data classes can be combined into a single Kotlin file.&lt;/p&gt;
&lt;h3&gt;More Intuitive Flow Control&lt;/h3&gt;
&lt;blockquote&gt;
&lt;p&gt;There are &lt;code&gt;if-else&lt;/code&gt; statements and there are &lt;code&gt;switch&lt;/code&gt; statements, then there are Python-like &lt;code&gt;if-else&lt;/code&gt; constructs and then there&apos;s Kotlin&apos;s &lt;code&gt;when&lt;/code&gt; construct.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;A &lt;code&gt;when&lt;/code&gt; improves the traditional &lt;code&gt;switch&lt;/code&gt; operator with more readable syntax and increased flexibility for different cases. It can be used both as a statement and as an expression. Following is an example:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;when (x) {
  in 1..10 -&amp;gt; print(&amp;quot;x is in the range&amp;quot;)
  in validNumbers -&amp;gt; print(&amp;quot;x is valid&amp;quot;)
  !in 10..20 -&amp;gt; print(&amp;quot;x is outside the range&amp;quot;)
  is String -&amp;gt; print(&amp;quot;x is not a number!&amp;quot;)
  else -&amp;gt; print(&amp;quot;wtf are you feeding me?&amp;quot;)
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;On a side note, just like the &lt;code&gt;when&lt;/code&gt; operator, the &lt;code&gt;if-else&lt;/code&gt; statements also return values in Kotlin.&lt;/p&gt;
&lt;p&gt;For looping, we can use &lt;code&gt;in&lt;/code&gt; operator instead of less meaningful &lt;code&gt;:&lt;/code&gt; from Java:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;for (item in array) {
  print(item)
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Alternatively, you can use the withIndex library function:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;for ((index, value) in array.withIndex()) {
  println(&amp;quot;the element at $index is $value&amp;quot;)
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Smart Casting&lt;/h3&gt;
&lt;p&gt;We often write &lt;code&gt;instanceof&lt;/code&gt; checks before processing a variable e.g. &lt;code&gt;if (x instanceof String) doSomething()&lt;/code&gt;. In Kotlin, the compiler is smart enough to cast objects when it is certain about type changes. The most common use case of this feature is when you have to use a value after checking it&apos;s type:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;if (u is User) {
  print(u.email)
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;If you were to write same snippet in Java, you&apos;d have to either cast &lt;code&gt;u&lt;/code&gt; once again in the print statement or either store the casted value for future use. In this instance, Kotlin can keep track of type and let you use it freely without caring about the type.&lt;/p&gt;
&lt;p&gt;Oh! and by the way &lt;code&gt;is&lt;/code&gt; and &lt;code&gt;as&lt;/code&gt; are also operators in Kotlin.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;readability += 1&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;Extension Functions&lt;/h3&gt;
&lt;p&gt;Here&apos;s a scenario, you need a way to check if user is connected to the Internet. You&apos;d find a snippet and then eventually end up storing that in some sort of &lt;code&gt;Utils&lt;/code&gt; class. Now this &lt;code&gt;Utils&lt;/code&gt; class has one more habitant along with GOD-knows-how-many other helper methods that are completely unrelated to each other and you carry this file with you in every project and making calls to it for all sorts of functionality. Sounds familiar?&lt;/p&gt;
&lt;p&gt;What if you could simply define that functionality in a class that is related to it? Without needing to inherit from it (in case it is a &lt;code&gt;final&lt;/code&gt; class)? So you can have that functionality in, say &lt;code&gt;Context&lt;/code&gt; class and then you can simply call your &lt;code&gt;isConnected()&lt;/code&gt; method from your activities as if it is part of them?
This is exactly what extension functions are for.&lt;/p&gt;
&lt;p&gt;To declare an extension function, we need to prefix its name with a receiver type, i.e. the type being extended. So our &lt;code&gt;isConnected()&lt;/code&gt; function will look something like this:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;fun Context.isConnected(): Boolean {
  // some logic to check if device is connected to the Internet
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;and then you can use this from inside any descendant of &lt;code&gt;Context&lt;/code&gt; class as if it is part of standard library while still keeping it in some sort of &lt;code&gt;Extensions&lt;/code&gt; or &lt;code&gt;Utils&lt;/code&gt; file.&lt;/p&gt;
&lt;h3&gt;Improved Lambdas&lt;/h3&gt;
&lt;p&gt;We all love lambdas, right? They provide clean and concise logical units that can be passed around as a plug-in functionality. While Java is catching up, they&apos;re still not up to the mark when it comes to multi-method lambdas and storing them as expressions.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;val sum = { x: Int, y: Int -&amp;gt; x + y }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This is a simple lambda expression that takes two integer type values and returns their sum. There&apos;s a lot of flexibility to them as well, for example, you can omit the method parentheses if the lambda is the only or last argument of a method.&lt;/p&gt;
&lt;h3&gt;Higher-Order Functions&lt;/h3&gt;
&lt;p&gt;Higher-Order Functions, something we always wanted in Java. If you are unfamiliar with the concept, it is simply a function that can accept other functions as parameters or returns a function.&lt;/p&gt;
&lt;p&gt;This is something that can eat a lot of your boilerplate code if used smartly. For example, let&apos;s build upon our existing &lt;code&gt;isConnected()&lt;/code&gt; function and create a function that accepts a block of code and executes it only when connected to Internet. This is a fairly common use case, so you may want to make that an extension function too.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;fun Context.doIfNetworkConnected(operation: () -&amp;gt; Unit) {
  if (isConnected()) {
    operation()
  } else {
    // Notify user about no connection
    displayAlert(&amp;quot;Please connect to a network!&amp;quot;)
  }
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;When combined with extension functions and lambdas, Higher-Order functions can make your trivial tasks such as checking for the Internet or closing the database after its usage so simple that you can forget about their existence.&lt;/p&gt;
&lt;h3&gt;Destructuring Declarations&lt;/h3&gt;
&lt;p&gt;I found this a very impressive feature in Python that sadly lacked in Java. What destructuring declarations do is simply chop down an object into a number of variables so you wont have to fetch each of its members individually and assign them to variables.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;val (name, email, password) = user&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;When combined with &lt;strong&gt;Data Classes&lt;/strong&gt;, this also paves way for functions that can return multiple values. Something we always wished to have in Java.&lt;/p&gt;
&lt;p&gt;For example, let&apos;s say we want return &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;message&lt;/code&gt; from an HTTP response, all we have to do is declare a &lt;code&gt;Data Class&lt;/code&gt; and return those two items as it&apos;s members. At the other end, we can simply break them.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;data class Response(val status: Int, val message: String)

fun handleHttpResponse(r:HttpResponse): Response {
  return Response(r.status, r.message)
}

// Now, to use this function:
val (status, message) = handleHttpResponse(someHttpResponse)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Default and Named Parameters&lt;/h3&gt;
&lt;p&gt;I think most of you would agree that if we had default parameters in Java, it could&apos;ve saved us from writing countless overloaded methods. We typically write one methods that covers all the possible parameters and then write other variations for it that doesn&apos;t do much else but delegate the call to the original method after providing an implementation for missing (or newly added) parameters.&lt;/p&gt;
&lt;p&gt;Kotlin solves this problem. Here&apos;s an example of extension function for &lt;code&gt;AppCompatActivity&lt;/code&gt; that displays a &lt;code&gt;Snackbar&lt;/code&gt; with short duration when the duration is not provided.&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;fun AppCompatActivity.showMessage(
  container: ViewGroup,
  message: String,
  length: Int = Snackbar.LENGTH_SHORT
) = Snackbar.make(container, message, length).show()&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Not only we can give default values to parameters but also use them with their name, so we can call them independent of their order of declaration. For example, I can call my &lt;code&gt;showMessage()&lt;/code&gt; function in any of the given orders:&lt;/p&gt;
&lt;deckgo-highlight-code language=&quot;kotlin&quot; theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;showMessage(container = c, message = &amp;quot;Hello!&amp;quot;, length = Snackbar.LENGTH_LONG)
// or
showMessage(message = &amp;quot;Hello!&amp;quot;, container = c, length = Snackbar.LENGTH_LONG)
// or
showMessage(message = &amp;quot;Hello!&amp;quot;, length = Snackbar.LENGTH_LONG, container = c)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;100% Java Interoperability&lt;/h3&gt;
&lt;p&gt;Last but most importantly, &lt;a href=&quot;https://kotlinlang.org/docs/reference/java-interop.html&quot;&gt;Kotlin is 100% interoperable with Java&lt;/a&gt;{:target=&quot;_blank&quot;}, which means you can use both languages side by side inside the same project and call code from each other without any special treatment.&lt;/p&gt;
&lt;p&gt;Important to mention that Android Studio 3 ships with a Java-to-Kotlin converter, that converts your Java classes with single click of a button. Save it for the time when you completely fall in love with it and decide to convert your existing codebase to Kotlin. ;)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;I tried to keep this post as concise as possible and skipped a lot of handy feature such as &lt;a href=&quot;https://kotlinlang.org/docs/reference/ranges.html&quot;&gt;Ranges&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;https://kotlinlang.org/docs/reference/properties.html&quot;&gt;Properties&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;https://kotlinlang.org/docs/reference/operator-overloading.html&quot;&gt;Operator Overloading&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;https://kotlinlang.org/docs/reference/basic-types.html&quot;&gt;Type Inference&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;https://stackoverflow.com/questions/23086291/format-in-kotlin-string-templates&quot;&gt;String Interpolation&lt;/a&gt;{:target=&quot;_blank&quot;} etc in doing so. However, I believe only those discussed will be enough to make you realize how much productivity and ease of use Kotlin can bring to your everyday development. Happy learning! :)&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;References&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://kotlinlang.org/docs/reference/&quot;&gt;Official Kotlin Reference&lt;/a&gt;{:target=&quot;_blank&quot;}&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://android-developers.googleblog.com/2017/05/android-announces-support-for-kotlin.html&quot;&gt;Android Developers Blog&lt;/a&gt;{:target=&quot;_blank&quot;}&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;Image Source - &lt;a href=&quot;https://commons.wikimedia.org/wiki/File:Kotlin_vs_java.jpg&quot;&gt;Wikimedia&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;br /&gt;
For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;</content:encoded></item><item><title><![CDATA[DevLog 02: Automating Wish Lists | Smarter Shopping with Price Monitoring]]></title><description><![CDATA[Remember when Mark Zuckerberg unveiled his AI assistant during the Christmas break last December? Everyone (including me) was super excited…]]></description><link>https://www.zuhaibahmad.com/dev-log-2/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/dev-log-2/</guid><pubDate>Sun, 06 Aug 2017 01:10:00 GMT</pubDate><content:encoded>&lt;p&gt;Remember when Mark Zuckerberg unveiled his &lt;a href=&quot;https://www.facebook.com/zuck/videos/10103351034741311/&quot;&gt;AI assistant&lt;/a&gt; during the Christmas break last December? Everyone (including me) was super excited and many people got specially interested in automation related stuff. That&apos;s when I wrote the &lt;a href=&quot;/dev-log-1/&quot;&gt;first post&lt;/a&gt; of this series pledging to work on a small scale automation projects and writing about them along the way.&lt;/p&gt;
&lt;p&gt;Well.. as expected, life happened and all the crazy ideas along with all the excitement got lost somewhere in the abyss of procrastinated projects. Only recently, I found time (and motivation) to start the project once again and made the first &lt;em&gt;&quot;actual&quot;&lt;/em&gt; automation script.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;Online shopping has made life a lot easier for us couch potatoes around the world. We lay around half-dead, scroll few pages on online stores and summon the products we want at our doorsteps. I, however, am among those cheapskates who wait for the sales seasons for the fulfilment of their wish-lists.&lt;/p&gt;
&lt;p&gt;Seemingly, these stores know about the existence of our species too. Many times, a lot of our desired items doesn&apos;t observe any discounts even in the sales seasons. Even when this isn&apos;t the case, impatience goes hands-in-hand with laziness anyway.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&quot;Life is too short to wait for sales seasons&quot; - Cheapo McCheapAss&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Frustrated with not being able to buy an item for months, I came up with this idea of a script that could sit silently at my system and monitor the prices for the items I need in the background and notify me whenever the prices go below the specified baseline.&lt;/p&gt;
&lt;p&gt;With little googling, I found some useful tools for data scraping with &lt;code&gt;Python&lt;/code&gt; (didn&apos;t had any prior experience of Python programing before this) which made things very straight forward. The basic recipe consists of following steps:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Define a structured list of items along with your desired baseline prices.&lt;/li&gt;
&lt;li&gt;Write a web scraper for the chosen online store to fetch the search results.&lt;/li&gt;
&lt;li&gt;Match the prices for scraped items against those in the wish-list.&lt;/li&gt;
&lt;li&gt;Display windows notifications and write the &quot;ready to buy&quot; items to an Excel file along with the product links.&lt;/li&gt;
&lt;li&gt;Schedule the script to run at windows start up.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Through the rest of the post I&apos;ll be demonstrating the procedure. As prerequisites, you&apos;ll need Python along with &lt;a href=&quot;https://pypi.python.org/pypi/pip&quot;&gt;pip&lt;/a&gt; package manager installed on your system. I&apos;d highly recommend using &lt;a href=&quot;https://www.continuum.io/downloads&quot;&gt;Anaconda&lt;/a&gt; as your default Python as it has almost all of the required modules pre-installed. To keep things simple, I&apos;ll limit the code to just a single online store, but hopefully, with little Python knowledge you can expand the script to work with as many store as you want.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;After starting to learn just a week ago, I consider myself absolutely noob at Python right now. So if you see something stupid in the code, please feel free to correct me because I only managed to write a &quot;working&quot; system with the limited knowledge I have.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;Step 01 : Defining The Wish-List&lt;/h2&gt;
&lt;p&gt;JSON is now a go-to format for structured data representation. We&apos;ll be using it to define our so called &quot;wish-list&quot;. Every item in the list will consist of following 3 attributes:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;description&lt;/code&gt;: A common description for the product that you can find on most of the stores for the specific product. Since the search results on websites usually contains a lot of irrelevant stuff along with the required items. This description will help us identify the similar items from search results.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;name&lt;/code&gt;: This will be our search term that we will send along with the search query.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;baseline_price&lt;/code&gt;: This will be the price at which we want to be notified about this item.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;I have allowed the &lt;em&gt;K&lt;/em&gt; notation as a shorthand for baseline_price prices in the &lt;code&gt;JSON&lt;/code&gt;, so we can optionally specify prices as 1K, 5K etc. instead of whole numbers&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The &lt;code&gt;wishlist.json&lt;/code&gt; file will look something like this:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;{
&amp;quot;items&amp;quot;: [
{
&amp;quot;description&amp;quot;: &amp;quot;Sapphire Radeon R9 270x&amp;quot;,
&amp;quot;name&amp;quot;: &amp;quot;Sapphire R9&amp;quot;,
&amp;quot;baseline_price&amp;quot;: &amp;quot;12K&amp;quot;
}, {
&amp;quot;description&amp;quot;: &amp;quot;Gigabyte LGA 1151 Z170&amp;quot;,
&amp;quot;name&amp;quot;: &amp;quot;Gigabyte Z170&amp;quot;,
&amp;quot;baseline_price&amp;quot;: &amp;quot;13200&amp;quot;
}
]
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now, once the system is setup, all you have to do is manage this file to get notifications for desired items.&lt;/p&gt;
&lt;h2&gt;Step 02 : Writing The Web Scraper&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Web Scrapers&lt;/strong&gt; are programs used to extract large amounts of data from websites whereby the data is extracted and saved to a local file in your computer or to a database in a structured format.&lt;/p&gt;
&lt;p&gt;Our simple scraper will send request with our search queries to the specified websites and then pick the target &lt;code&gt;HTML&lt;/code&gt; attributes from the returned results. You will have to examine the source of websites you want to scrap before writing the code for them. Most of them represent data in a structured way with consistent &lt;code&gt;Ids&lt;/code&gt; and &lt;code&gt;classes&lt;/code&gt;. Once the target tags have been picked, all you need to do is extracting the their values and parse them to relevant data structures.&lt;/p&gt;
&lt;p&gt;To make things easier, Python has a brilliant and easy to use library for data scraping called &lt;a href=&quot;https://en.wikipedia.org/wiki/Beautiful_Soup_(HTML_parser)&quot;&gt;Beautiful Soup&lt;/a&gt;. We will be using it to write our scraper.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
class AmazonScraper: # Declare search URL and class names to picked
BASE_URL = &amp;#39;https://www.amazon.com/s/ref=nb_sb_noss/&amp;#39; \
 &amp;#39;134-4639554-0290304?url=search-alias%3Daps&amp;amp;&amp;#39; \
 &amp;#39;field-keywords={}&amp;#39;
PRODUCT_TITLE_CLASS = &amp;quot;a-link-normal s-access-detail-page &amp;quot; \
 &amp;quot;s-color-twister-title-link &amp;quot; \
 &amp;quot;a-text-normal&amp;quot;
PRODUCT_BRAND_CLASS = &amp;quot;a-size-small a-color-secondary&amp;quot;
PRODUCT_PRICE_CLASS = &amp;quot;a-size-base a-color-base&amp;quot;

    def search_item(self, product):
        # Read the page contents
        query = urllib.parse.quote(product.name)
        url = self.BASE_URL.format(query)
        # Get structured data using beautiful soup
        data = bSoup(requests.get(url).text, &amp;quot;html.parser&amp;quot;)

        # Find all the item containers
        containers = data.findAll(
            &amp;quot;div&amp;quot;, {&amp;quot;class&amp;quot;, &amp;quot;a-fixed-left-grid-col a-col-right&amp;quot;}
        )

        # Get item information for each item in container
        if len(containers) &amp;gt; 0:
            for item in containers:
                title_div = item.find(
                    &amp;quot;a&amp;quot;, {&amp;quot;class&amp;quot;, self.PRODUCT_TITLE_CLASS}
                )
                price_div = item.find(
                    &amp;quot;span&amp;quot;, {&amp;quot;class&amp;quot;, self.PRODUCT_PRICE_CLASS}
                )
                brand_div = item.findAll(
                    &amp;quot;span&amp;quot;, {&amp;quot;class&amp;quot;, self.PRODUCT_BRAND_CLASS}
                )[1]

                title = title_div.text
                brand = brand_div.text
                price = self.extract_price(price_div)
                link = title_div[&amp;quot;href&amp;quot;]

                # Display Windows Notification and write the product
                # details to a csv file when the Price picked form
                # the search result is less than or equal to our
                # baseline price for the item and the product&amp;#39;s
                # descriptions has a similarity rate of more than 45%
                price = math.floor(float(price))
                baseline_price = float(product.baseline_price)
                if baseline_price &amp;gt;= price &amp;gt; 0:
                    prompt = &amp;quot;\&amp;quot;&amp;quot; + title.replace(&amp;quot;,&amp;quot;, &amp;quot;|&amp;quot;) + \
                             &amp;quot;\&amp;quot; is now available in &amp;quot; + \
                             str(price) + &amp;quot; at Amazon (Baseline:&amp;quot; + \
                             &amp;quot; &amp;quot; + product.baseline_price + &amp;quot;)&amp;quot;
                    details = get_details(brand, price, title, link)
                    if is_similar(title, product.description):
                        print_similarity(title, product.description)
                        display_windows_notification(brand, prompt)
                        write_to_csv(details)

    @staticmethod
    def extract_price(price_div):
        if price_div is None:
            return 0
        # Remove all the symobls received with the price text
        price = re.sub(r&amp;#39;[–|-||]&amp;#39;, &amp;quot;&amp;quot;, price_div.text).strip()
        price = price.replace(&amp;quot;$&amp;quot;, &amp;quot;&amp;quot;)
        # Convert USD to PKR
        price = float(price) * 100
        return math.floor(price)
&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Step 03 : Run Scrapper for each Product in Wish-List&lt;/h2&gt;
&lt;p&gt;Once the main component of our system i.e. scraper is in place. We need to set up a routine that could read from our &lt;code&gt;wishlist.json&lt;/code&gt; file and parse its contents into data models. These model object will then be passed to our scrapper for further processing.&lt;/p&gt;
&lt;p&gt;In the procedure, we&apos;ll also be creating a &lt;code&gt;available_wishlist_products.csv&lt;/code&gt; file that will carry all the latest ready-to-purchase items.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;class Product:
def **init**(self, product):
self.description = product[&amp;quot;description&amp;quot;]
self.name = product[&amp;quot;name&amp;quot;]
self.baseline_price = product[&amp;quot;baseline_price&amp;quot;] # Allowed number representation in &amp;#39;K&amp;#39; format # (e.g. 3K, 10K etc) in json. Here while parsing, # convert the prices with K notation to numeric value
has_k = str(self.baseline_price).lower().**contains**(&amp;quot;k&amp;quot;)
price = self.baseline_price[:-1]
price = int(price) \* 1000
self.baseline_price = price if has_k else self.baseline_price

# Read all the items from the wishlist.json

with open(&amp;#39;wishlist.json&amp;#39;) as data_file:
items = json.load(data_file)

# Create csv file to write available items to

filename = &amp;quot;available_wishlist_products.csv&amp;quot;
f = open(filename, &amp;quot;w&amp;quot;)
headers = &amp;quot;Date, Title, Brand, Price, Link\n&amp;quot;
f.write(headers)
f.close()

# Parse JSON into Product objects and pass them to scraper for search

scraper = AmazonScraper()
for i in items[&amp;quot;items&amp;quot;]:
p = Product(i)
scraper.search_item(p)
time.sleep(5)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Step 04 : Writing The Utility Functions&lt;/h2&gt;
&lt;p&gt;You might have noticed calls to many unknown functions in the script we have written so far (e.g. &lt;code&gt;print_similarity&lt;/code&gt;, &lt;code&gt;display_windows_notification&lt;/code&gt;, &lt;code&gt;write_to_csv&lt;/code&gt;). In the last part of the script, we&apos;ll be writing these utility functions.&lt;/p&gt;
&lt;h3&gt;Get Item Details&lt;/h3&gt;
&lt;p&gt;This utility function will prepare the data for writing to &lt;code&gt;csv&lt;/code&gt; file by reformatting the text and generating a coma separated string from them.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
def get_details(brand, price, title, link):
details = title.replace(&amp;quot;,&amp;quot;, &amp;quot;|&amp;quot;) + &amp;quot;, &amp;quot; + \
 brand.replace(&amp;quot;,&amp;quot;, &amp;quot;|&amp;quot;) + &amp;quot;, &amp;quot; + \
 str(math.floor(float(price))) + &amp;quot;, &amp;quot; + \
 link
return details&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Write Details To CSV File&lt;/h3&gt;
&lt;p&gt;As the name suggests, this function will open our output file and write the contents of available items to it along with the date.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice that this time we open the file with &lt;code&gt;a&lt;/code&gt; parameter (for appending) unlike in the main routine where we recreate the file every time with a &lt;code&gt;w&lt;/code&gt; flag.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;def write_to_csv(details):
file = open(&amp;quot;available_wishlist_products.csv&amp;quot;, &amp;quot;a&amp;quot;)
time = strftime(&amp;quot;%a, %d %b %Y&amp;quot;, gmtime())
details = time.replace(&amp;quot;,&amp;quot;, &amp;quot; &amp;quot;) + &amp;quot;, &amp;quot; + details + &amp;quot;\n&amp;quot;
file.write(details)
file.close()
print(&amp;quot;\nProduct Details: &amp;quot;)
print(details)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Check Similarity Between The Product Descriptions&lt;/h3&gt;
&lt;p&gt;Since search results often bring a lot of irrelevant results, it is important that we check for the resemblance between the descriptions of our define product and the item received from search results. I have set a threshold of 45% for similarity in descriptions to make an item qualify as legitimate result.&lt;/p&gt;
&lt;p&gt;Now as you may have guessed, checking for alikeness between two set of texts seems a very difficult task. Thankfully, Python comes to rescue once again with its &lt;a href=&quot;https://docs.python.org/2/library/difflib.html&quot;&gt;difflib&lt;/a&gt;.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
def is_similar(a, b):
similarity_percent = SequenceMatcher(None, a, b).ratio() \* 100
return similarity_percent &amp;gt; 45&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This function is pretty much useless but helps debug when ran from console. You can omit it from your code if you don&apos;t want to print results to the console.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;def print_similarity(a, b):
similarity_percent = SequenceMatcher(None, a, b).ratio() \* 100
print(&amp;quot;Similarity % between &amp;quot; + b + &amp;quot; and &amp;quot; + a + &amp;quot;: &amp;quot; +
str(similarity_percent))&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Displaying Windows Notifications&lt;/h3&gt;
&lt;p&gt;The last part is to display Windows toast notifications for new available items. If you were using &lt;strong&gt;Anaconda&lt;/strong&gt;, this will be the only module that you&apos;ll have to install yourself:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
pip install win10toast&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;We&apos;re simply creating an instance of &lt;code&gt;ToastNotifier&lt;/code&gt; and supplying it a title and message to display along with option icon path and a duration.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;def display_windows_notification(title, msg):
print(msg)
notification = ToastNotifier()
notification.show_toast(
title, msg, icon_path=ICON_PATH, duration=30
)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;That was all the code required, only thing left is the scheduling of this task. The final will look like this:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
import time
import json
import math
import re
import urllib.parse
from time import strftime, gmtime
import requests
from bs4 import BeautifulSoup as bSoup
from win10toast import ToastNotifier
from difflib import SequenceMatcher

# Path to icon file to display with the notifications

ICON_PATH = &amp;quot;&amp;lt;Path to .ico File&amp;gt;&amp;quot;

class AmazonScraper: # Declare search URL and class names to picked
BASE_URL = &amp;#39;https://www.amazon.com/s/ref=nb_sb_noss/&amp;#39; \
 &amp;#39;134-4639554-0290304?url=search-alias%3Daps&amp;amp;&amp;#39; \
 &amp;#39;field-keywords={}&amp;#39;
PRODUCT_TITLE_CLASS = &amp;quot;a-link-normal s-access-detail-page &amp;quot; \
 &amp;quot;s-color-twister-title-link &amp;quot; \
 &amp;quot;a-text-normal&amp;quot;
PRODUCT_BRAND_CLASS = &amp;quot;a-size-small a-color-secondary&amp;quot;
PRODUCT_PRICE_CLASS = &amp;quot;a-size-base a-color-base&amp;quot;

    def search_item(self, product):
        # Read the page contents
        query = urllib.parse.quote(product.name)
        url = self.BASE_URL.format(query)
        # Get structured data using beautiful soup
        data = bSoup(requests.get(url).text, &amp;quot;html.parser&amp;quot;)

        # Find all the item containers
        containers = data.findAll(
            &amp;quot;div&amp;quot;, {&amp;quot;class&amp;quot;, &amp;quot;a-fixed-left-grid-col a-col-right&amp;quot;}
        )

        # Get item information for each item in container
        if len(containers) &amp;gt; 0:
            for item in containers:
                title_div = item.find(
                    &amp;quot;a&amp;quot;, {&amp;quot;class&amp;quot;, self.PRODUCT_TITLE_CLASS}
                )
                price_div = item.find(
                    &amp;quot;span&amp;quot;, {&amp;quot;class&amp;quot;, self.PRODUCT_PRICE_CLASS}
                )
                brand_div = item.findAll(
                    &amp;quot;span&amp;quot;, {&amp;quot;class&amp;quot;, self.PRODUCT_BRAND_CLASS}
                )[1]

                title = title_div.text
                brand = brand_div.text
                price = self.extract_price(price_div)
                link = title_div[&amp;quot;href&amp;quot;]

                # Display Windows Notification and write the product
                # details to a csv file when the Price picked form
                # the search result is less than or equal to our
                # baseline price for the item and the product&amp;#39;s
                # descriptions has a similarity rate of more than 45%
                price = math.floor(float(price))
                baseline_price = float(product.baseline_price)
                if baseline_price &amp;gt;= price &amp;gt; 0:
                    prompt = &amp;quot;\&amp;quot;&amp;quot; + title.replace(&amp;quot;,&amp;quot;, &amp;quot;|&amp;quot;) + \
                             &amp;quot;\&amp;quot; is now available in &amp;quot; + \
                             str(price) + &amp;quot; at Amazon (Baseline:&amp;quot; + \
                             &amp;quot; &amp;quot; + product.baseline_price + &amp;quot;)&amp;quot;
                    details = get_details(brand, price, title, link)
                    if is_similar(title, product.description):
                        print_similarity(title, product.description)
                        display_windows_notification(brand, prompt)
                        write_to_csv(details)

    @staticmethod
    def extract_price(price_div):
        if price_div is None:
            return 0
        price = re.sub(r&amp;#39;[–|-||]&amp;#39;, &amp;quot;&amp;quot;, price_div.text).strip()
        price = price.replace(&amp;quot;$&amp;quot;, &amp;quot;&amp;quot;)
        # Convert USD to PKR
        price = float(price) * 100
        return math.floor(price)

class Product:
def **init**(self, product):
self.description = product[&amp;quot;description&amp;quot;]
self.name = product[&amp;quot;name&amp;quot;]
self.baseline_price = product[&amp;quot;baseline_price&amp;quot;] # Allowed number representation in &amp;#39;K&amp;#39; format # (e.g. 3K, 10K etc) in json. Here while parsing, # convert the prices with K notation to numeric value
has_k = str(self.baseline_price).lower().**contains**(&amp;quot;k&amp;quot;)
price = self.baseline_price[:-1]
price = int(price) \* 1000
self.baseline_price = price if has_k else self.baseline_price

def get_details(brand, price, title, link):
details = title.replace(&amp;quot;,&amp;quot;, &amp;quot;|&amp;quot;) + &amp;quot;, &amp;quot; + \
 brand.replace(&amp;quot;,&amp;quot;, &amp;quot;|&amp;quot;) + &amp;quot;, &amp;quot; + \
 str(math.floor(float(price))) + &amp;quot;, &amp;quot; + \
 link
return details

def write_to_csv(details):
file = open(&amp;quot;available_wishlist_products.csv&amp;quot;, &amp;quot;a&amp;quot;)
time = strftime(&amp;quot;%a, %d %b %Y&amp;quot;, gmtime())
details = time.replace(&amp;quot;,&amp;quot;, &amp;quot; &amp;quot;) + &amp;quot;, &amp;quot; + details + &amp;quot;\n&amp;quot;
file.write(details)
file.close()
print(&amp;quot;\nProduct Details: &amp;quot;)
print(details)

def is_similar(a, b):
similarity_percent = SequenceMatcher(None, a, b).ratio() \* 100
return similarity_percent &amp;gt; 45

def print_similarity(a, b):
similarity_percent = SequenceMatcher(None, a, b).ratio() \* 100
print(&amp;quot;Similarity % between &amp;quot; + b + &amp;quot; and &amp;quot; + a + &amp;quot;: &amp;quot; +
str(similarity_percent))

def display_windows_notification(title, msg):
print(msg)
notification = ToastNotifier()
notification.show_toast(
title, msg, icon_path=ICON_PATH, duration=30
)

# Read all the items from the wishlist.json

with open(&amp;#39;wishlist.json&amp;#39;) as data_file:
items = json.load(data_file)

# Create csv file to write available items to

filename = &amp;quot;available_wishlist_products.csv&amp;quot;
f = open(filename, &amp;quot;w&amp;quot;)
headers = &amp;quot;Date, Title, Brand, Price, Link\n&amp;quot;
f.write(headers)
f.close()

# Parse JSON into Product objects and pass them to scraper for search

scraper = AmazonScraper()
for i in items[&amp;quot;items&amp;quot;]:
p = Product(i)
scraper.search_item(p) # IMPORTANT: Delay each request by 5 seconds # so Amazon wont suspect our bot
time.sleep(5)&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Step 05 : Scheduling Auto Execution Of The Script&lt;/h2&gt;
&lt;p&gt;Our system is now up and running, you can run it using Python from command line now. However, what&apos;s the fun in it if you have to run it yourself every time?&lt;/p&gt;
&lt;p&gt;In this final step, we&apos;ll set up our script to run at every windows start up.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Open a text editor and write the following cmd commands.&lt;/li&gt;
&lt;/ol&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;cd /d &amp;quot;&amp;lt;Path To Your Script Folder&amp;gt;&amp;quot;
start pythonw &amp;quot;&amp;lt;Script Name&amp;gt;.py&amp;quot;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ol start=&quot;2&quot;&gt;
&lt;li&gt;
&lt;p&gt;Save the file with &lt;code&gt;.bat&lt;/code&gt; extension. Try running the file and see if the program gets executed.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create a shortcut of this file and copy it to &lt;code&gt;C:\Documents and Settings\[User Name]\Start Menu\Programs\Startup&lt;/code&gt; if you are running older version of windows or otherwise to &lt;code&gt;C:\Users\[User Name]\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup&lt;/code&gt; if your are running Windows 10.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;That&apos;s all! Your script is now ready to execute at every windows start up. For a quick test, try signing out of your windows and sign in again.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;In less than 150 lines of code, we created a very sophisticated price monitoring system that is easy to use and scale. Since starting to write this post, I&apos;ve already expanded the script to work with 4 different local and international stores.&lt;/p&gt;
&lt;p&gt;While this is small system brings a lot of value, there are still some limitations to it. For example, the scrapper for &lt;a href=&quot;www.newegg.com&quot;&gt;NewEgg&lt;/a&gt; worked for a day or two before the website identified the scraper and placed a &quot;captcha&quot; between the results. Even this is not a dead-end, with the power of Python&apos;s text and image recognition libraries, these small barriers can be overcome with just a few lines of code. However, this means consistent maintenance and upgrading in order to keep the system working.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Source Code for this project is available at &lt;a href=&quot;https://github.com/xuhaibahmad/PELL&quot;&gt;GitHub repository&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;br /&gt;
For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;</content:encoded></item><item><title><![CDATA[Going Clean With MVP | A Guide To Model-View-Presenter On Android]]></title><description><![CDATA[Let's face it! All of us wrote tons and tons of code inside MainActivity.java, many still do... Writing code that "works" is fine and…]]></description><link>https://www.zuhaibahmad.com/go-clean-with-mvp-on-android/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/go-clean-with-mvp-on-android/</guid><pubDate>Sun, 21 May 2017 03:10:00 GMT</pubDate><content:encoded>&lt;blockquote&gt;
&lt;p&gt;Let&apos;s face it! All of us wrote tons and tons of code inside &lt;code&gt;MainActivity.java&lt;/code&gt;, many still do...&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Writing code that &lt;em&gt;&quot;works&quot;&lt;/em&gt; is fine and acceptable as you start working with a new language or framework. After all, the whole point is to get the work done, right? Not exactly!&lt;/p&gt;
&lt;p&gt;We all start from the same place where every &quot;hello world&quot; marks an accomplishment. However, as you grow as an experienced developer, you start to realize that object-oriented programming is more than just writing the code that &lt;em&gt;works&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Almost every project requires some level of testing and maintenance throughout its lifecycle. This makes it a strict requirement to write modular code that is easy to read, modify, test and maintain for future collaborators.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. - John Woods&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Obviously, our &lt;em&gt;&quot;I love &lt;code&gt;MainActivity&lt;/code&gt;&quot;&lt;/em&gt; guy won&apos;t find this an easy task. Why? Because when you write every single piece of code in the class where it is needed. You end up with a codebase which of course, gets the job done, but makes it almost impossible to test and maintain in the longer run.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Model-View-Presenter&lt;/h2&gt;
&lt;p&gt;MVP is an architectural pattern that attempts to decouple the business logic from the views by providing simpler activities with all the important work behind separate presentation layers. This results in a highly readable, maintainable and most importantly testable code.&lt;/p&gt;
&lt;p&gt;Some might argue that MVP is not an architectural pattern per se as it is only responsible for managing the presentation of the data. However, we leave the debate here and move on to the &quot;&lt;em&gt;why?&lt;/em&gt;&quot; aspect.&lt;/p&gt;
&lt;h2&gt;Need for MVP?&lt;/h2&gt;
&lt;p&gt;Android activities are cluttered with closely coupled components where everything is structured in a &lt;em&gt;&quot;throw the code on the wall&quot;&lt;/em&gt; manner. This makes it an intrinsic problem for android code to be hard to test and extend. Anyone who went through the pain of learning android application testing can tell you that in the beginning it almost feels impossible to test android activities due to unmockable &lt;em&gt;(is this even a word?)&lt;/em&gt; SDK components and lifecycle management complexities.&lt;/p&gt;
&lt;p&gt;When you separate the business logic away from the activity in a separate presentation layer, it automatically makes it trivial to mock and test your code the way you would test any other java class.&lt;/p&gt;
&lt;p&gt;The other difficulty apart from code testing is the maintenance of existing codebase. If you are reading this post and understand what I have said so far, I am assuming that you are at that point where you are familiar with the &lt;a href=&quot;https://en.wikipedia.org/wiki/SOLID_%28object-oriented_design%29&quot;&gt;SOLID&lt;/a&gt;{:target=&quot;blank&quot;} principles of object oriented design by &lt;a href=&quot;https://en.wikipedia.org/wiki/Robert_Cecil_Martin&quot;&gt;Robert C. Martin a.k.a Uncle Bob&lt;/a&gt;{:target=&quot;blank&quot;}. The last part of the five principles refers to the &lt;em&gt;Dependency Inversion Principle (DIP)&lt;/em&gt; which encourages the programmers to write code in a way that &lt;strong&gt;it depends upon abstractions instead of the concrete implementations&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;The way MVP is implemented nowadays (or at least they way I learned to) is through &lt;code&gt;Contract&lt;/code&gt; interfaces that describes the responsibilities of &lt;code&gt;Views&lt;/code&gt; and the &lt;code&gt;Presenters&lt;/code&gt;, making the maintenance of code as simple as adding a new method to the interface and its implementation in the corresponding class. Note that this decoupling also allows us for interchangeable views for the same business logic.&lt;/p&gt;
&lt;p&gt;Now let&apos;s put a full-stop to the theoretical stuff and explore the implementation details of Model, View and Presenter by building a very basic &lt;strong&gt;&lt;em&gt;hello world MVP app&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Before we go ahead, it&apos;s important that you understand that there is no &lt;strong&gt;&lt;em&gt;&quot;ultimate&quot;&lt;/em&gt;&lt;/strong&gt; or &lt;strong&gt;&lt;em&gt;&quot;best&quot;&lt;/em&gt;&lt;/strong&gt; way of implementing MVP. Most people use their own implementations with some modifications, which is completely acceptable as long as the end goal is achieved. Which is to structure your code to decouple the implementation logic from the presentation layer. The way I&apos;m doing it is only the way I like it, if you find another variation of it, don&apos;t confuse and use the style that suits you.&lt;/p&gt;
&lt;h2&gt;The Model&lt;/h2&gt;
&lt;p&gt;Model is an interface responsible for managing data. Model’s responsibilities include using APIs, caching data, managing databases and so on. The model can also be an interface that communicates with other modules in charge of these responsibilities.&lt;/p&gt;
&lt;p&gt;For the sake of simplicity, I&apos;m not using any model classes in this example but I hope you got the idea.&lt;/p&gt;
&lt;h2&gt;The View&lt;/h2&gt;
&lt;p&gt;Do not confuse this with your &lt;code&gt;XML&lt;/code&gt; views. Views in MVP are simply the components responsible for managing the user interface. This includes all the UI manipulation operations such as setting text to some &lt;code&gt;TextView&lt;/code&gt;, toggling visibility of &lt;code&gt;Views&lt;/code&gt;, displaying animations etc. Basically, everything that has to do with the User Interface, goes into this place.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Keep your views as dumb as possible.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The idea is to keep your &lt;code&gt;Presenters&lt;/code&gt; unaware of &quot;how&quot; to present data to the user. All they have is a &lt;code&gt;View&lt;/code&gt; instance that takes this data as input and performs the presentation tasks. So if you were to display an error to the user in case he forgets to fill a field in the form, all your presenter has to do is call the &lt;code&gt;view.displayError(errorMessage)&lt;/code&gt; method on the &lt;code&gt;View&lt;/code&gt; instance.&lt;/p&gt;
&lt;p&gt;Now realize that the &lt;code&gt;Presenter&lt;/code&gt; doesn&apos;t know what happens next, whether the error is displayed as a &lt;code&gt;Toast&lt;/code&gt;, or as a &lt;code&gt;Dialog&lt;/code&gt; or a &lt;code&gt;Snackbar&lt;/code&gt; or what color is the message etc. The &lt;code&gt;View&lt;/code&gt; on the other hand does not know anything about the data it is provided and its origins, it is only interested in accepting the supplied data and manipulate the UI accordingly. This makes it very easy to mock &lt;code&gt;Views&lt;/code&gt; while testing your business logic so you can focus on the stuff that is important.&lt;/p&gt;
&lt;p&gt;The way I like to define my &lt;code&gt;Views&lt;/code&gt; is through a &lt;em&gt;View interface&lt;/em&gt; which resides inside the &lt;em&gt;Contract Interface&lt;/em&gt; for that particular activity.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;interface View extends BaseView&amp;lt;Presenter&amp;gt; {

    void displayMessage(String text);

    void displayResult(int result);

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now all I have to do is create a new class and make it implement this interface and we have a candidate that can serve as View for our activity. I guess you might have guessed that we can provide multiple implementations of View as well for different use cases.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public class MainActivity extends AppCompatActivity implements ContractMain.View{

    @BindView(R.id.toolbar)
    Toolbar mToolbar;
    @BindView(R.id.container)
    CoordinatorLayout mContainer;
    @BindView(R.id.numberOneEditText)
    EditText mNumberOneEditText;
    @BindView(R.id.numberTwoEditText)
    EditText mNumberTwoEditText;
    @BindView(R.id.resultTextView)
    TextView mResultTextView;

    private ContractMain.Presenter mPresenter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        setSupportActionBar(mToolbar);

        mPresenter = new MainPresenter(this, this);
        mPresenter.start();
    }

    @Override
    public void displayMessage(String text) {
        Snackbar.make(mContainer, text, Snackbar.LENGTH_LONG).show();
    }

    @Override
    public void setPresenter(ContractMain.Presenter presenter) {
        mPresenter = presenter;
    }

    @Override
    public ContractMain.Presenter getPresenter() {
        return mPresenter;
    }

    @OnClick(R.id.addButton)
    public void onAddButtonClicked() {
        String numberOne = mNumberOneEditText.getText().toString();
        String numberTwo = mNumberTwoEditText.getText().toString();

        mPresenter.performAddition(numberOne, numberTwo);
    }

    @Override
    public void displayResult(int result) {
        mResultTextView.setText(String.valueOf(result));
    }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;The Presenter&lt;/h2&gt;
&lt;p&gt;Think of the presenters as the &lt;strong&gt;&lt;em&gt;Boss&lt;/em&gt;&lt;/strong&gt; here. Similar to how you used to manage everything from your activity in the &lt;strong&gt;&lt;em&gt;&lt;code&gt;I Love MainActivity&lt;/code&gt;&lt;/em&gt;&lt;/strong&gt; era, Presenters now handles all the management tasks and keeps your activities/fragments less busy. So instead of throwing your &lt;code&gt;DatabaseHandler&lt;/code&gt;, &lt;code&gt;ApiHandler&lt;/code&gt;, &lt;code&gt;PeanutButterMaker&lt;/code&gt; and instances of whatnot in the activity class and make it look like a huge pile of crappy code, you just provide it with the &lt;code&gt;Presenter&lt;/code&gt; instance and place all the other important stuff inside your &lt;code&gt;Presenter&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Now just like the Views, presenters are also defined through a child interface inside the contract interface of the activity that owns it.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;interface Presenter extends BasePresenter {

    void performAddition(String numberOne, String numberTwo);

    boolean isEmptyInput(String numOne, String numTwo);

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Now all I need to do is to make my activity/fragment implement this interface and provide implementations for the defined operations.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
class MainPresenter implements ContractMain.Presenter {

    private final ContractMain.View mView;
    private final Context mContext;

    MainPresenter(ContractMain.View view, Context context) {
        mView = view;
        mContext = context;
    }

    @Override
    public void start() {
        // Do your initialization work here
    }

    @Override
    public void performAddition(String numberOne, String numberTwo) {

        if (isEmptyInput(numberOne, numberTwo)) {
            // Display error message if any of the inputs is empty
            mView.displayMessage(mContext.getString(R.string.error_empty_input));
        } else {
            // Compute and pass the result to view for display
            final int firstNumber = Integer.parseInt(numberOne);
            final int secondNumber = Integer.parseInt(numberTwo);

            final int result = firstNumber + secondNumber;
            mView.displayResult(result);
        }
    }

    @Override
    public boolean isEmptyInput(String numOne, String numTwo) {
        return numOne == null || numOne.length() == 0 ||
            numTwo == null || numTwo.length() == 0;
    }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;hr&gt;
&lt;h2&gt;Outcome&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;So you might be wondering what did we achieve apart from dividing our activity into 3 pieces?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The first benefit of using this approach is that we achieved a more modular and scalable architecture. If you were to add &lt;strong&gt;subtract&lt;/strong&gt; functionality to this activity, all you would do is define a new method &lt;code&gt;void performSubtraction(String numberOne, String numberTwo)&lt;/code&gt; in the &lt;code&gt;Presenter&lt;/code&gt; interface and provide its implementation. Your contracts are the documentation for your code itself.&lt;/p&gt;
&lt;p&gt;The second and most important change is that our activity is now fully testable. You could simply mock the View and test all the application logic using your &lt;code&gt;Presenter&lt;/code&gt;. Below are a couple of tests for the activity we implemented earlier. I&apos;m using &lt;code&gt;Mockito&lt;/code&gt; for mocking dependencies.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;@RunWith(MockitoJUnitRunner.class)
public class MainTest {

    private static final String EMPTY_INPUT_ERROR_MESSAGE
        = &amp;quot;Please Provide Both Numbers!&amp;quot;;

    @Mock
    private ContractMain.View mView;
    @Mock
    private Context mContext;

    private ContractMain.Presenter mPresenter;

    @Before
    public void setUp() throws Exception {
        mPresenter = new MainPresenter(mView, mContext);
    }

    @Test
    public void performAddition_shouldReturnResult() {
        final String numberOne = String.valueOf(2);
        final String numberTwo = String.valueOf(3);

        mPresenter.performAddition(numberOne, numberTwo);

        verify(mView).displayResult(5);
    }

    @Test
    public void performAddition_shouldDisplayErrorMessage() {
        final String numberOne = String.valueOf(2);
        final String numberTwo = null;
        // Mock error string because our mock context
        // wouldn&amp;#39;t be able to return it
        when(mContext.getString(R.string.error_empty_input))
            .thenReturn(EMPTY_INPUT_ERROR_MESSAGE);

        mPresenter.performAddition(numberOne, numberTwo);

        verify(mView).displayMessage(EMPTY_INPUT_ERROR_MESSAGE);
    }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;hr&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;At this point, it may make sense to your or it won&apos;t, but sooner or later, you&apos;ll realize that you need some level of modularization in your code if it requires proper testing and be able to remain maintainable for long-term. If not MVP, you might go with one of the several other architectural patterns that are available. For me, MVP makes more sense than any other in the context of Android.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;sub&gt;Image rights belong to macoscope.&lt;/sub&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Source Code is available at &lt;a href=&quot;https://github.com/xuhaibahmad/MVP-Tutorial&quot;&gt;GitHub repository&lt;/a&gt;{:target=&quot;blank&quot;}.&lt;br /&gt;
For suggestions and queries, just &lt;a href=&quot;http://linkedin.com/in/xuhaibahmad&quot;&gt;contact me&lt;/a&gt;{:target=&quot;blank&quot;}.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;</content:encoded></item><item><title><![CDATA[ActiveAndroid Testing with Robolectric]]></title><description><![CDATA[Testing has been painful for android developers from day one. The problem lies probably in the way the foundations of the android system has…]]></description><link>https://www.zuhaibahmad.com/activeandroid-testing-with-robolectric/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/activeandroid-testing-with-robolectric/</guid><pubDate>Sun, 19 Feb 2017 04:42:00 GMT</pubDate><content:encoded>&lt;p&gt;Testing has been painful for android developers from day one. The problem lies probably in the way the foundations of the android system has been laid. Over the years, many significant improvements have been made yet Android remains one of the most &lt;em&gt;hard-to-test&lt;/em&gt; platforms to date.&lt;/p&gt;
&lt;p&gt;Due to the fact that Android is open-sourced and has a &lt;strong&gt;&lt;em&gt;HUGE&lt;/em&gt;&lt;/strong&gt; community of skilled and creative developers. They left no stone unturned in their attempts to make things better. From new libraries and strategies for testing to the varying styles of organizing and architecting the code (e.g. &lt;a href=&quot;https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93presenter&quot;&gt;MVP&lt;/a&gt;{:target=&quot;_blank&quot;} and &lt;a href=&quot;https://8thlight.com/blog/uncle-bob/2012/08/13/the-clean-architecture.html&quot;&gt;Clean&lt;/a&gt;{:target=&quot;_blank&quot;} architecture). We have seen attempts made to tackle the problem from every possible angle.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://www.robolectric.org&quot;&gt;Robolectric&lt;/a&gt;{:target=&quot;_blank&quot;} was one of the revolutionizing tools that paved way for &lt;a href=&quot;https://en.wikipedia.org/wiki/Test-driven_development&quot;&gt;TDD&lt;/a&gt;{:target=&quot;_blank&quot;} on Android. It helps you run most of the tests that requires an Android Device or emulator (Services, Database, UI etc) locally on &lt;a href=&quot;https://en.wikipedia.org/wiki/Java_virtual_machine&quot;&gt;JVM&lt;/a&gt;{:target=&quot;_blank&quot;} in a matter of seconds.&lt;/p&gt;
&lt;p&gt;Another brilliant (and popular these days) library that made lives easier was the &lt;a href=&quot;www.activeandroid.com&quot;&gt;ActiveAndroid&lt;/a&gt;{:target=&quot;_blank&quot;} &lt;a href=&quot;https://en.wikipedia.org/wiki/Object-relational_mapping&quot;&gt;ORM&lt;/a&gt;{:target=&quot;_blank&quot;} that removes all the boilerplate code required to even write the simplest databases in Android while using traditional &lt;a href=&quot;https://www.sqlite.org/&quot;&gt;SQLite&lt;/a&gt;{:target=&quot;_blank&quot;}. However, testing remains as troublesome as it is with the old lad that we are upgrading from.&lt;/p&gt;
&lt;p&gt;Luckily, ActiveAndroid comes in with a partial support form temporary in-memory databases which combined with the power of Robolectric, makes testing no different from your everyday unit tests. In this post, we&apos;ll learn to integrate both libraries to setup a testing environment for our database.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#adding-dependencies&quot;&gt;Adding Dependencies&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#writing-a-simple-app&quot;&gt;Writing A Simple App&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#testing-with-robolectric&quot;&gt;Testing with Robolectric&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Adding Dependencies&lt;/h2&gt;
&lt;p&gt;Before we begin, we need to include Robolectric and ActiveAndroid to our project along with some other necessary libraries. Add the following dependencies to your &lt;span class=&quot;evidence&quot;&gt;app/build.gradle&lt;/span&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// Support libraries
compile &amp;#39;com.android.support:design:25.1.0&amp;#39;
compile &amp;#39;com.android.support:appcompat-v7:25.1.0&amp;#39;
// Active Android
compile &amp;#39;com.michaelpardo:activeandroid:3.1.0-SNAPSHOT&amp;#39;
// JUnit
testCompile &amp;#39;junit:junit:4.12&amp;#39;
// Robolectric
testCompile &amp;#39;org.robolectric:robolectric:3.2.2&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Writing A Simple App&lt;/h2&gt;
&lt;p&gt;We&apos;ll start by writing a very simple application that would allow us to save and retrieve information to and from our ActiveAndroid database.&lt;/p&gt;
&lt;h4&gt;MainActivity.java&lt;/h4&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public class MainActivity extends AppCompatActivity
implements View.OnClickListener {

    private LinearLayout mContainer;
    private EditText mNameEditText;
    private EditText mEmailEditText;
    private EditText mContactEditText;
    private Button mSaveButton;
    private Button mRetrieveButton;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mContainer = (LinearLayout) findViewById(R.id.LLContainer);
        mNameEditText = (EditText) findViewById(R.id.ETName);
        mEmailEditText = (EditText) findViewById(R.id.ETEmail);
        mContactEditText = (EditText) findViewById(R.id.ETContact);
        mSaveButton = (Button) findViewById(R.id.BTSave);
        mRetrieveButton = (Button) findViewById(R.id.BTRetrieve);

        mSaveButton.setOnClickListener(this);
        mRetrieveButton.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        int id = v.getId();

        String name = mNameEditText.getText().toString();
        String email = mEmailEditText.getText().toString();
        String contact = mContactEditText.getText().toString();

        if (id == R.id.BTSave) {
            // Validate input
            if (isEmpty(name) || isEmpty(email) || isEmpty(contact)){
                showSnackBar(&amp;quot;Please Fill All Fields Before Saving&amp;quot;);
                return;
            }

            // Save to database
            boolean isSuccessful = DatabaseUtils.getInstance()
                    .saveToDatabase(name, email, contact);

            // Notify user regarding operation success
            String message = isSuccessful ? &amp;quot;Saved Successfully&amp;quot;
                : &amp;quot;Error While Saving Item&amp;quot;;
            showSnackBar(message);

        } else if (id == R.id.BTRetrieve) {
            // Validate input
            if (isEmpty(name) &amp;amp;&amp;amp; isEmpty(email) &amp;amp;&amp;amp; isEmpty(contact)){
                showSnackBar(&amp;quot;Please Fill At Least One Field &amp;quot; +
                    &amp;quot;Before Retrieving From Database&amp;quot;);
                return;
            }

            // Retrieve from database
            Item item = DatabaseUtils.getInstance()
                    .retrieveFromDatabase(name, email, contact);

            // Notify user about error in case of failure
            if (item == null) {
                showSnackBar(&amp;quot;Error While Retrieving Item&amp;quot; +
                    &amp;quot; From Database&amp;quot;);
                return;
            }

            // Display item data in relevant fields
            mNameEditText.setText(item.getName());
            mEmailEditText.setText(item.getEmail());
            mContactEditText.setText(item.getContact());

        } else {
            showSnackBar(&amp;quot;Invalid Action!&amp;quot;);
        }
    }

    private void showSnackBar(String message) {
        Snackbar.make(mContainer, message, Snackbar.LENGTH_SHORT)
            .show();
    }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h4&gt;activity_main.xml&lt;/h4&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
&amp;lt;?xml version=&amp;quot;1.0&amp;quot; encoding=&amp;quot;utf-8&amp;quot;?&amp;gt;

&amp;lt;LinearLayout
    xmlns:android=&amp;quot;http://schemas.android.com/apk/res/android&amp;quot;
    xmlns:tools=&amp;quot;http://schemas.android.com/tools&amp;quot;
    android:id=&amp;quot;@+id/LLContainer&amp;quot;
    android:layout_width=&amp;quot;match_parent&amp;quot;
    android:layout_height=&amp;quot;match_parent&amp;quot;
    android:orientation=&amp;quot;vertical&amp;quot;
    android:paddingBottom=&amp;quot;@dimen/activity_vertical_margin&amp;quot;
    android:paddingLeft=&amp;quot;@dimen/activity_horizontal_margin&amp;quot;
    android:paddingRight=&amp;quot;@dimen/activity_horizontal_margin&amp;quot;
    android:paddingTop=&amp;quot;@dimen/activity_vertical_margin&amp;quot;&amp;gt;

    &amp;lt;LinearLayout
        android:layout_width=&amp;quot;match_parent&amp;quot;
        android:layout_height=&amp;quot;0dp&amp;quot;
        android:layout_weight=&amp;quot;1&amp;quot;
        android:gravity=&amp;quot;center&amp;quot;
        android:orientation=&amp;quot;vertical&amp;quot;&amp;gt;

        &amp;lt;android.support.design.widget.TextInputLayout
            android:layout_width=&amp;quot;match_parent&amp;quot;
            android:layout_height=&amp;quot;wrap_content&amp;quot;&amp;gt;

            &amp;lt;EditText
                android:id=&amp;quot;@+id/ETName&amp;quot;
                android:layout_width=&amp;quot;match_parent&amp;quot;
                android:layout_height=&amp;quot;wrap_content&amp;quot;
                android:ems=&amp;quot;10&amp;quot;
                android:hint=&amp;quot;Name&amp;quot;
                android:inputType=&amp;quot;textPersonName&amp;quot;/&amp;gt;
        &amp;lt;/android.support.design.widget.TextInputLayout&amp;gt;

        &amp;lt;android.support.design.widget.TextInputLayout
            android:layout_width=&amp;quot;match_parent&amp;quot;
            android:layout_height=&amp;quot;wrap_content&amp;quot;&amp;gt;

            &amp;lt;EditText
                android:id=&amp;quot;@+id/ETEmail&amp;quot;
                android:layout_width=&amp;quot;match_parent&amp;quot;
                android:layout_height=&amp;quot;wrap_content&amp;quot;
                android:ems=&amp;quot;10&amp;quot;
                android:hint=&amp;quot;Email&amp;quot;
                android:inputType=&amp;quot;textEmailAddress&amp;quot;/&amp;gt;
        &amp;lt;/android.support.design.widget.TextInputLayout&amp;gt;

        &amp;lt;android.support.design.widget.TextInputLayout
            android:layout_width=&amp;quot;match_parent&amp;quot;
            android:layout_height=&amp;quot;wrap_content&amp;quot;&amp;gt;

            &amp;lt;EditText
                android:id=&amp;quot;@+id/ETContact&amp;quot;
                android:layout_width=&amp;quot;match_parent&amp;quot;
                android:layout_height=&amp;quot;wrap_content&amp;quot;
                android:ems=&amp;quot;10&amp;quot;
                android:hint=&amp;quot;Contact Number&amp;quot;
                android:inputType=&amp;quot;phone&amp;quot;/&amp;gt;
        &amp;lt;/android.support.design.widget.TextInputLayout&amp;gt;
    &amp;lt;/LinearLayout&amp;gt;

    &amp;lt;LinearLayout
        android:layout_width=&amp;quot;match_parent&amp;quot;
        android:layout_height=&amp;quot;wrap_content&amp;quot;
        android:orientation=&amp;quot;horizontal&amp;quot;&amp;gt;

        &amp;lt;Button
            android:id=&amp;quot;@+id/BTSave&amp;quot;
            android:layout_width=&amp;quot;0dp&amp;quot;
            android:layout_height=&amp;quot;wrap_content&amp;quot;
            android:layout_margin=&amp;quot;4dp&amp;quot;
            android:layout_weight=&amp;quot;.5&amp;quot;
            android:background=&amp;quot;@color/colorPrimary&amp;quot;
            android:text=&amp;quot;Save Contact&amp;quot;
            android:textColor=&amp;quot;@android:color/white&amp;quot;/&amp;gt;

        &amp;lt;Button
            android:id=&amp;quot;@+id/BTRetrieve&amp;quot;
            android:layout_width=&amp;quot;0dp&amp;quot;
            android:layout_height=&amp;quot;wrap_content&amp;quot;
            android:layout_margin=&amp;quot;4dp&amp;quot;
            android:layout_weight=&amp;quot;.5&amp;quot;
            android:background=&amp;quot;@color/colorPrimary&amp;quot;
            android:text=&amp;quot;Retrieve Contact&amp;quot;
            android:textColor=&amp;quot;@android:color/white&amp;quot;/&amp;gt;
    &amp;lt;/LinearLayout&amp;gt;

&amp;lt;/LinearLayout&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h4&gt;Item.java&lt;/h4&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
/\*\*

- Created by Zuhaib Ahmad on 2/7/2017.
- &amp;lt;p&amp;gt;
- Java POJO representing database model
  \*/
  public class Item extends Model {

      public static final String KEY_CONTACT = &amp;quot;contact&amp;quot;;
      public static final String KEY_EMAIL = &amp;quot;email&amp;quot;;
      public static final String KEY_NAME = &amp;quot;name&amp;quot;;

      @Column(name = KEY_NAME)
      private String name;
      @Column(name = KEY_EMAIL)
      private String email;
      @Column(name = KEY_CONTACT)
      private String contact;

      public Item() {
          super();
      }

      public Item(String name, String email, String contact) {
          super();
          this.name = name;
          this.email = email;
          this.contact = contact;
      }

      public String getName() {
          return name;
      }

      public void setName(String name) {
          this.name = name;
      }

      public String getEmail() {
          return email;
      }

      public void setEmail(String email) {
          this.email = email;
      }

      public String getContact() {
          return contact;
      }

      public void setContact(String contact) {
          this.contact = contact;
      }

  }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h4&gt;DatabaseUtils.java&lt;/h4&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;/\*\*

- Created by Zuhaib Ahmad on 1/28/2017.
- &amp;lt;p&amp;gt;
- Utility class to handle database operations
  \*/
  public class DatabaseUtils {

      private static DatabaseUtils instance;

      private DatabaseUtils() {
      }

      /**
       * Gets singleton instance.
       *
       * @return the instance
       */
      public static DatabaseUtils getInstance() {
          if (instance == null) {
              instance = new DatabaseUtils();
          }

          return instance;
      }

      /**
       * Retrieves {@link Item} object from database
       *
       * @param name    The name of contact
       * @param email   The email of contact
       * @param contact The number of contact
       * @return The retrieved item
       */
      public Item retrieveFromDatabase(String name, String email,
          String contact) {
          // Use first non-null parameter for conditional check
          String whereClause = &amp;quot;&amp;quot;;
          String key = &amp;quot;&amp;quot;;

          if (!isEmpty(name)) {
              whereClause = name;
              key = Item.KEY_NAME;
          } else if (!isEmpty(email)) {
              whereClause = email;
              key = Item.KEY_EMAIL;
          } else if (!isEmpty(contact)) {
              whereClause = contact;
              key = Item.KEY_CONTACT;
          }

          // Query the database and return the fetched item
          return new Select()
                  .from(Item.class)
                  .where(key + &amp;quot; =?&amp;quot;, whereClause)
                  .executeSingle();
      }

      /**
       * Saves {@link Item} to database
       *
       * @param name    The name of contact
       * @param email   The email of contact
       * @param contact The number of contact
       * @return The operation success status
       */
      public boolean saveToDatabase(String name, String email,
          String contact) {
          // Create database item object out of provided parameters
          Item item = new Item(name, email, contact);

          // Save to database and get the id of saved item
          Long id = item.save();

          // Mark operation as successful if returned id is non-zero
          return id &amp;gt; 0;
      }

  }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h4&gt;Application.java&lt;/h4&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
/\*\*

- Created by Zuhaib on 1/22/2016.
- &amp;lt;p&amp;gt;
- Application class for global management and data access
  \*/
  public class Application extends android.app.Application {

      @Override
      public void onCreate() {
          super.onCreate();
          // Create Active Android configurations
          Configuration.Builder configuration =
              new Configuration.Builder(this);
          configuration.addModelClasses(Item.class);
          // Initialize ActiveAndroid DB
          ActiveAndroid.initialize(configuration.create());
      }

  }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Testing with Robolectric&lt;/h2&gt;
&lt;p&gt;Before writing tests for our database, we will need to configure our temporary database. To achieve this, we will be writing an alternate version of our &lt;span class=&quot;evidence&quot;&gt;Application.java&lt;/span&gt; class which will replace the original in our tests.&lt;/p&gt;
&lt;h4&gt;TestApplication.java&lt;/h4&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;/\*\*

- Created by Zuhaib Ahmad on 1/20/2017.
- &amp;lt;p&amp;gt;
- Application subclass to be used in Tests
  \*/

public class TestApplication extends Application {

    @Override
    public void onCreate() {
        super.onCreate();
        // Create configurations for a temporary mock database
        Configuration.Builder configuration =
            new Configuration.Builder(this).setDatabaseName(null);
        configuration.addModelClasses(Item.class);
        // Initialize ActiveAndroid DB
        ActiveAndroid.initialize(configuration.create());
    }

    @Override
    public void onTerminate() {
        // Dispose temporary database on termination
        ActiveAndroid.dispose();
        super.onTerminate();
    }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;blockquote&gt;
&lt;p&gt;Notice the &lt;code&gt;setDatabaseName(null)&lt;/code&gt; while building configurations. It allows us to create an anonymous temporary database which we dispose on termination of application.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4&gt;DatabaseUtilsTest.java&lt;/h4&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
/\*\*

- Created by Zuhaib Ahmad on 2/7/2017.
- &amp;lt;p&amp;gt;
- Tests fro database utils
  \*/
  @RunWith(RobolectricTestRunner.class)
  @Config(
  constants = BuildConfig.class,
  // Run with TestApplication instead of actual
  application = TestApplication.class,
  // Test against Lollipop
  sdk = 21
  )
  public class DatabaseUtilsTest {

      private DatabaseUtils mInstance;

      @Before
      public void setUp() throws Exception {
          ShadowLog.stream = System.out;

          mInstance = DatabaseUtils.getInstance();
      }

      @Test
      public void retrieveFromDatabase() throws Exception {
          String name = &amp;quot;Test Retrieve Item&amp;quot;;
          String email = &amp;quot;Retrieve@abc.com&amp;quot;;
          String contact = &amp;quot;12345&amp;quot;;
          Item testItem = new Item(name, email, contact);
          testItem.save();

          // Fetch with name
          Item item1 = mInstance
              .retrieveFromDatabase(name, null, null);
          assertNotNull(item1);
          assertEquals(testItem, item1);

          // Fetch with email
          Item item2 = mInstance
              .retrieveFromDatabase(null, email, null);
          assertNotNull(item2);
          assertEquals(testItem, item2);

          // Fetch with contact
          Item item3 = mInstance
              .retrieveFromDatabase(null, null, contact);
          assertNotNull(item3);
          assertEquals(testItem, item3);

          testItem.delete();
      }

      @Test
      public void saveToDatabase() throws Exception {
          String name = &amp;quot;Test Save Item&amp;quot;;
          String email = &amp;quot;Save@abc.com&amp;quot;;
          String contact = &amp;quot;67890&amp;quot;;

          // Create test object from test parameters
          Item testItem = new Item(name, email, contact);

          // Save test parameters
          boolean isSuccessful = mInstance
              .saveToDatabase(name, email, contact);

          // Check if successfully saved
          assertTrue(isSuccessful);

          // Check if saved item is equal to test object
          Item savedItem = mInstance
              .retrieveFromDatabase(name, null, null);
          assertEquals(testItem.getName(), savedItem.getName());
          assertEquals(testItem.getEmail(), savedItem.getEmail());
          assertEquals(testItem.getContact(), savedItem.getContact());
      }

  }&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Run the tests and hopefully you&apos;ll see green lights. That&apos;s it, you can now locally test your databases just as you would unit test any other part of your application. You can find complete source code of the application &lt;a href=&quot;https://github.com/xuhaibahmad/Active-Android-Testing-With-Robolectric&quot;&gt;here&lt;/a&gt;{:target=&quot;_blank&quot;}&lt;/p&gt;</content:encoded></item><item><title><![CDATA[DevLog 01: Making A Smarter PC | Automating The Download Management]]></title><description><![CDATA[Everyone was hyped up after Mark Zuckerberg unveiled his AI assistant during the Christmas break last December. Almost everyone thought of…]]></description><link>https://www.zuhaibahmad.com/dev-log-1/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/dev-log-1/</guid><pubDate>Sun, 08 Jan 2017 04:10:00 GMT</pubDate><content:encoded>&lt;p&gt;Everyone was hyped up after Mark Zuckerberg unveiled his &lt;a href=&quot;https://www.facebook.com/zuck/videos/10103351034741311/&quot;&gt;AI assistant&lt;/a&gt; during the Christmas break last December. Almost everyone thought of building or at least owning something similar to &lt;a href=&quot;marvel-movies.wikia.com/wiki/J.A.R.V.I.S&quot;&gt;Jarvis&lt;/a&gt;. I have been a fan of &lt;a href=&quot;https://en.wikipedia.org/wiki/Artificial_intelligence&quot;&gt;AI&lt;/a&gt; and &lt;a href=&quot;https://en.wikipedia.org/wiki/Internet_of_things&quot;&gt;IoT&lt;/a&gt; for a long time now but after closely following several impressive automation projects last year such as Amazon&apos;s &lt;a href=&quot;https://www.amazon.com/b?node=16008589011&quot;&gt;Go&lt;/a&gt; and &lt;a href=&quot;https://es.wikipedia.org/wiki/Amazon_Echo&quot;&gt;Echo&lt;/a&gt;, I got pretty much obsessed with the idea of machines doing stuff &lt;em&gt;for or on behalf of you&lt;/em&gt; &lt;em&gt;(sounds cool right?)&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;So this year I decided to build a small automation system for my personal use. Something simple that would not only be fun but could also help me in everyday life. For this purpose, I compiled a list of &lt;strong&gt;&lt;em&gt;do-able&lt;/em&gt;&lt;/strong&gt; tasks that can be done (or already exist) with the help of some programming. My initial plan is to write small scripts for each separate task and then integrate them into once piece of software once I have a collection. Starting with this post, I&apos;ll be going through each item on that list in the order of lowest to highest difficulty. Obviously, I might not be able to achieve everything on that list but still it&apos;s worth a try and I&apos;ll be documenting my progress in these &lt;a href=&quot;http://zuhaibahmad.com/tags/#devlog&quot;&gt;DevLogs&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Problem&lt;/h2&gt;
&lt;p&gt;I&apos;m a huge fan of &lt;a href=&quot;www.imdb.com/title/tt3107288/&quot;&gt;Flash&lt;/a&gt; and &lt;a href=&quot;www.imdb.com/title/tt2193021/&quot;&gt;Arrow&lt;/a&gt; and desperately wait every week for new episodes. However, the problem is that there&apos;s no &lt;a href=&quot;www.cwtv.com/&quot;&gt;CW TV&lt;/a&gt; (the channel that broadcasts them) in my country, so the only option left for me is via torrents. &lt;em&gt;Not a problem! Right?&lt;/em&gt; &lt;strong&gt;&lt;em&gt;Nope!&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Then I face another problem. When you are in a place where the &lt;strong&gt;&lt;em&gt;&quot;unlimited&quot;&lt;/em&gt;&lt;/strong&gt; connection gives a download speed of &lt;strong&gt;~200Kbps&lt;/strong&gt;, choosing between doing your work or putting something on download becomes a matter of trading off one thing for another. My solution (and surely for most of the people living with a shitty connection) is to put your stuff on download at night before going to bed and leave the system turned on.&lt;/p&gt;
&lt;p&gt;Now this strategy works pretty well but for a forgetful soul of a software engineer, this too is a task that must go into the &lt;strong&gt;to-do list&lt;/strong&gt; in order to avoid the chances of being brushed off my head. So the first item I picked from my list was to make my computer do most of the downloading work for me while I sleep at night. To make the task more helpful, I expanded the scope to not only torrents but also &lt;strong&gt;Steam&lt;/strong&gt; and &lt;strong&gt;IDM&lt;/strong&gt;. The process is straight forward:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make the computer turn on by itself at a specific time (from sleep or hibernation).&lt;/li&gt;
&lt;li&gt;Start necessary programs.&lt;/li&gt;
&lt;li&gt;Continue pending downloads.&lt;/li&gt;
&lt;li&gt;Go back to sleep/shutdown at some specific time in the morning.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So I started my research and what I found surprising was that you don&apos;t need to write any code to accomplish this. In the next steps I&apos;ll try to demonstrate how I did it for my system:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Please note that this is only the way I achieved my desired results, there might be much more ways to build even smarter, more complex and efficient systems.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2&gt;Step 01 : Enabling Wake Timers&lt;/h2&gt;
&lt;p&gt;Before you set up your computer for timed wake up and shutdown, make sure you have the &lt;strong&gt;Wake Timers&lt;/strong&gt; enabled. Wake Timers allows Windows to wake up on timed events. By default, they are disabled on Windows 7 and above. To enable them, following the instruction below.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Right-click the battery icon and click &lt;strong&gt;Power Options&lt;/strong&gt; or go to &lt;strong&gt;Control Panel&lt;/strong&gt; and select &lt;strong&gt;Power Options&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 215px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2ef4568717a8de7897a50c31411293f5/2eb24/power_options.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 51.87500000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAKCAYAAAC0VX7mAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACMklEQVR42qWLS08aURzFJxrdCLHpACW6EOnaL9ZdXfQDaBp5d9900aRp+m6q1leUOCDjwPAYBxgQahUtLbQEUTpUhpvL8c5Eu27Sm/vLOTnnf7j7Ho/unZkxGP3/YnZ2MDc31+buORyEARMXz/8bd3ncbv7icmHW69W5xYUF4vf54FtaQjAYRCQcRjgUtrypTyIRhCwfsrqQqTeZubslEAggFA7rXLd7SWC9IY4Oi1AySVRLeXyvn+GkWkYpr6B2VEW5qELNydBUBRUtj2ajcTMbsj+0LCFE5+RSjWRqHWSPW4gXTrGZ/oJYsc78GYT8GXYOTiCWG4jmvmJVLEJQa9jOVBHX6lBOO8jVzhlt5i9QqLV07pn0kywmDPhFHQ9f5DD/UrF49E7Dg6dxlmUx/0rF0l4PkTRFMDlAMEUQkAz4xCsLv/gHj0UDz7NdndsuNslyScdq6RIflV94K9fxIdfEJ7WF19IJ3qS+4X3mB5YLF1iv9LCidS1dP+yxze8bulgp9yBUOjrXOW8TSgwMSR+qkkU2k0ZGTiF/oOD4qIJ9MYFsOoVEXMChVkClrLFeZJkMo98H6ADmnhkM+lc6t7GxQRRFQTKZhJSUsLW1BVEUkUiI2BV2sS9JWPu8hk2Wm1k8vgdBELATjbI+BlmWkWKYXSwW07mxsTHD4/EMeZ6nTqeT2mw2Oj09Td1ut4XX66Xj4+PUNjFBHbyDTk7esbDb7XRkZIROTU1Z96Ojo2BZ9xoQ0RzMPLgwQwAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Power Options&quot;
        title=&quot;&quot;
        src=&quot;/static/2ef4568717a8de7897a50c31411293f5/2eb24/power_options.png&quot;
        srcset=&quot;/static/2ef4568717a8de7897a50c31411293f5/69538/power_options.png 160w,
/static/2ef4568717a8de7897a50c31411293f5/2eb24/power_options.png 215w&quot;
        sizes=&quot;(max-width: 215px) 100vw, 215px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Click on the &lt;strong&gt;Change Plan Settings&lt;/strong&gt; link of your active plan.&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 590px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/27156c99bd65c03251fc4bd9e6ea80ff/fcda8/change_plan_settings.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 43.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAJCAYAAAAywQxIAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABYklEQVR42o1RC26DMBTj/hdsRTdaOjKgfBIIkEAInpNq2qRp05DMMw4yzyaxG/zmngh8dQGe3EcN+Bd2BOL9NblkJbK8Ql40OL0IvJKn1M5ZhRu1Xg3oehnRSwWlFKSUGLXGOI4YhoGaPKyxwfCWCPGGqiohRIGiIC8FiqrFtR5xrQakQuHlXSEtaKg05mmCJpZlgVlmWBPnsTvG8QcNCxoJgbIsaSiQ53c0TRsS4MDvl+ehYdDFAZP9fJWGTdPgfD7jcrkQKU6nE7Isg3pGQdf1kYeYPXmI2bUtyrpD/tBQK9DO/stwXdf4kmYnG9c+vIfbNgR947R2JWx8DjNqxmCaF0htYDaPybjPMLckdHG/33HLc7T8ctg4TMkf8Hg8uGEXtb7vqXeo6xoLzcJOcvEYmLubvhvyUGsWrcdY9MGjg7c/8bPSKO37nieMa6VSlh3ZaZotExP+X9i/YJxzbMunHx2jtS6UEoXsAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Change Plan Settings&quot;
        title=&quot;&quot;
        src=&quot;/static/27156c99bd65c03251fc4bd9e6ea80ff/fcda8/change_plan_settings.png&quot;
        srcset=&quot;/static/27156c99bd65c03251fc4bd9e6ea80ff/69538/change_plan_settings.png 160w,
/static/27156c99bd65c03251fc4bd9e6ea80ff/72799/change_plan_settings.png 320w,
/static/27156c99bd65c03251fc4bd9e6ea80ff/fcda8/change_plan_settings.png 590w&quot;
        sizes=&quot;(max-width: 590px) 100vw, 590px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In next window, click &lt;strong&gt;Change Advanced Power Settings&lt;/strong&gt; link.&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 580px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/a4a9496a58e62bb774a39d2ca2d1940d/b6272/advance_settings.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 66.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAANCAYAAACpUE5eAAAACXBIWXMAAA7DAAAOwwHHb6hkAAABxElEQVR42q1T2Y7bMAz0/39gUbTJ5nDsjS1Zp+VDljSl1OzW2SJAH0pgQOrgkCKpal5C1KNPxm1JWZ+sI3vKOq/XJM2aQkQCnhFjJDztZfGV1Dad6x7XhuF8Y6hbVuxL3aF5l2g7BW1GTJOD0grjOCKEgBeyVd77NM9TcViWCfM0wjmLlfZa2aAb7ui7Dkop2ndFd11Pdg4y/U2YUycBqYJsp8cLvvXfceFXOOMwz3PZG63B/X6HEALGGJD3MyEXNkmlIQaBvu9LBlmyffxxoOx68GH4PDudToRzIWScgxOeCIWySWtDkW2JmJ2MsZBS4nA8oGkbaDmAMQ5D9y6XayHPRHLg5V7aE1JdkiUy+yDMWJYF1kgwchwEEb9TkNwQq8BZB601HD39SE3jYtg1OTdl1UkISp2enKN2PcNGXQzeUT0jNqrrT+6B6OFXg7DNVDcUkjfhYZwhO/4h3NP/B9kqu8TUjdQEAnO/kdfrbtTSl5BlEvJkFB2L/Uko6WfU0qMhZN0qsgnChZL8h/NXvMwwhPyFEsIOH+tXRLkpTdOgJTDGaDJkmU36RVtFl8Kjqv8MIoy3262ACCONTqzrOtHErL8AWxjxTMQjx5UAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Advance Settings&quot;
        title=&quot;&quot;
        src=&quot;/static/a4a9496a58e62bb774a39d2ca2d1940d/b6272/advance_settings.png&quot;
        srcset=&quot;/static/a4a9496a58e62bb774a39d2ca2d1940d/69538/advance_settings.png 160w,
/static/a4a9496a58e62bb774a39d2ca2d1940d/72799/advance_settings.png 320w,
/static/a4a9496a58e62bb774a39d2ca2d1940d/b6272/advance_settings.png 580w&quot;
        sizes=&quot;(max-width: 580px) 100vw, 580px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Under the &lt;strong&gt;Advanced Settings&lt;/strong&gt; window, expand &lt;strong&gt;Sleep&lt;/strong&gt; option and then expand &lt;strong&gt;Allow Wake Timers&lt;/strong&gt; option. Under &lt;strong&gt;Plugged In&lt;/strong&gt;, change &lt;strong&gt;Disable&lt;/strong&gt; to &lt;strong&gt;Enable&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 381px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/e9e195b87a1db4d729de70f989bbaf3d/2add2/allow_wake_timers.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 111.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAA7EAAAOxAGVKw4bAAADCElEQVR42o1V23baQAzk/5/61L/ob5DTQELCLcZgg80dDAaDjS/gqUYG0qSUhHOEvOzuWKORRKluvmC6mCI+xAjDEPv9HtnxiO9+sixDnuc4yh1aae7NsPSW2Gw28MSvVisFvW/hP78xGFpp4/sYui4mkwkcx8FwOCo2eTCkhdfILxc/P/NsEAQaVClNU5wk5FzCjw4HbLdb3aAvDvnw/bWu9/udPu8IsA3k2VfAzWar9OM4RolIFecJP379hGs7qDfqaDYaaIh/rddRfXpGs9mQ51fxTbRaLfQsC6bZQUPW7XYb88XiHTA/5VhspqiZNeyCHSzbQq/XgytpsPs2mgSQ9WAwwEEYpEmi/iCXoyhS2sfj6R0wkQPL5VLMw2g0lhwOxY/giTik5C0LodbrtaaCdjqdPkqd4x0wTROlVH54wPPzM56EYq1WuwJTLHquXXcokTpg3lkqBL4Y1wrIr2C3x3g80twQoNvtwupZ6JhdGIYhtCKllWVHtTTNrsY196ezOchWAVlXo6GrlxM5FEWhFumlYOnvfXiG6dEImWC+5SCdMrBtdAS0KyIEIhD3jt/oGp7r9weIGeEFMBLAvqhqGB2lbBhvsCxbfk+KvN+Jkkp3OmZBWaMgYBRL3npK27Zt9fPZDPlnRf8DaEoQZ8D4mmiWxEjUJOVe18RgOIXrRUiOXwN2TPNvwOy6OR6PpUsaaLcacBcBqk4Eb5+dad8GZBmxW65lQ7ozoee6jopimkX4qZRKlObi76vMOtxK3zM4Bdxsdxj0+zA7IoiETlFm8zkcaTc2/1eikOFqtT63XpzI9AgxnU6F7kQLe732tVh3UvAULdH+jaV/JeoklfXZhGoqYKxjtq7mkPw/tCVHWf6xUVmLM+kE4+1NW7RSraJSqeDx92+Uy2VJkYm5MLrmsAC5bZeXMFLORw6KYkj4WJ+HBlVmdAp4GaS3jBcpFscXJ7nneRoJ67UvOV8JGIcGc16cGaJUjK7bRjDOwboMWhY6/yIsqyd0H2UiveiaA7cqKeCkIvU/vsWLiDCtAaEAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Allow Wake Timers&quot;
        title=&quot;&quot;
        src=&quot;/static/e9e195b87a1db4d729de70f989bbaf3d/2add2/allow_wake_timers.png&quot;
        srcset=&quot;/static/e9e195b87a1db4d729de70f989bbaf3d/69538/allow_wake_timers.png 160w,
/static/e9e195b87a1db4d729de70f989bbaf3d/72799/allow_wake_timers.png 320w,
/static/e9e195b87a1db4d729de70f989bbaf3d/2add2/allow_wake_timers.png 381w&quot;
        sizes=&quot;(max-width: 381px) 100vw, 381px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Step 02 : Scheduling Wake Up Tasks&lt;/h2&gt;
&lt;p&gt;The &lt;strong&gt;Task scheduler&lt;/strong&gt; is a powerful yet underestimated feature that comes with Windows OS. It enables you to automatically perform routine tasks on your computer based on whatever criteria you have set. We&apos;ll be using Task Scheduler to configure our computer for the automatic wake up and shutdown on specific times. Let&apos;s first set up &lt;strong&gt;timed wake up&lt;/strong&gt;.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open up Task Scheduler by typing in &lt;code&gt;task Scheduler&lt;/code&gt; in your Windows Start Menu search bar.&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 375px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/2b295b34b489ecdec6d6ee56717ac1b3/5ff7e/open_ts.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 155.625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAfCAYAAADnTu3OAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADFUlEQVR42rVWW09TQRBekyZcKoJtDXJ9AGxi40uDJmpsJMaYPugzP0cFSq0iKbTFpCYg+HsIfSIEwRIKJFxbetteTvu5M3Jqi0BbqZNMTs/Z3W9nvvlmt6Kzs1MzGAxaIBDQAGjxeFzL5XJaJpPRstmslkgk+HuxWLzQaa6UUsvn87xG9PT0oL29HXNzc0ilUgiHw0in0zg6OsLh4SFWV1dBphaj3PT309NTbG9vI5lMQgFDDA0O4lZbG4LBIE9Qu/CA2pEXkRcKhUtdn0Mm1VrxYuQ5nj19jG8L89jd3cPm5iZHFY1GL4zsKsvl8hCPhodxb2AA35eWfu8ikyrKLEdJrnhk1zStKiBlJfr6+tDR0QGfP4DwTgQ/f4Sws7OLlZUV5nN9fZ1dFatqxKpAEAMqOiqMz+dDgUiOJ5FWxaGinJyc4Pj4uKboSoAmkwlCCAYkSyYTiMViPFivccoWi4UBZ2dnWS4kgbW1NZZB/UXJ/QGcmZlhHVJ1SX+1pnkpIEVIRuTv7+9zZa+dMnEXCoW4EPWmS2DcKTqgXhRKV/UvT6C0KQ3qiOpWrEyZAPMqoL091S0bG6y9ra0tLC8vs4RqibgiZQKkHajZz/ev3q9XAdI84r0CkDisNZrzps4vZMo59Pv9JQ4jkQgTTBUnPulJTt/qSpmMojw4OOD0SdwkdtIn/a4mpQpAPUL6+K92KaDOXzmP1YryV6c0IsL/A6gfX+rWawCHZwdsf39/6XC4dlG6u7thNBpLsrk2oLro0aau0YYB2mw22O12TE9PN6AoZTr0er38kVqNOkPvkHQ6xc9ypzF9vNyjqssEXaF669HBSncKCTh7dg6qvy5noi6QupUXOAu9DXmMAlGtGVMnlbBarSAeR0dHOW232w2Xy4XF+XlMffqIr0Gvep+E58MUJiY9eO/+jC9KYosLCxgfe4dJ1wS8njF4Jt7CMz4G4XQ6pcPhkGazWSpNSkWBbGpqlm9eO2XXXYt88vKhNLa2yC6zRRpaTbLljlXabfflK8eINDcL2Xu7SfbeFLL1hpAP7EPyF+zOdVKbaM5PAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Open Task Scheduler&quot;
        title=&quot;&quot;
        src=&quot;/static/2b295b34b489ecdec6d6ee56717ac1b3/5ff7e/open_ts.png&quot;
        srcset=&quot;/static/2b295b34b489ecdec6d6ee56717ac1b3/69538/open_ts.png 160w,
/static/2b295b34b489ecdec6d6ee56717ac1b3/72799/open_ts.png 320w,
/static/2b295b34b489ecdec6d6ee56717ac1b3/5ff7e/open_ts.png 375w&quot;
        sizes=&quot;(max-width: 375px) 100vw, 375px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Now, let&apos;s create our first task. Click on &lt;strong&gt;Action&lt;/strong&gt; ➜ &lt;strong&gt;Create Task&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 292px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/9c94a5cb10e81d12af056f6979af1c1d/2e9f9/create_task.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 88.75%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAASCAYAAABb0P4QAAAACXBIWXMAAA7DAAAOwwHHb6hkAAADM0lEQVR42pVU247bVBTN5yMQr6CWF15AoIonEKUFCQEzLS1NOkl8t+PEdhzfcvM9aTOzWPtkOnToC1ha2j7b3uvsyzpn4LomonCB+cxDEkdY+C48x8Z6vYY8Nzc3+D/PIElSFEUBa7nBcJbjhbWCGRbYrAv0hwNOpxPKssTxePxvhJsiwzIMoadHPLp08N3zGZztCasoQBAuFZFh6IhXqw8ylvfr6+t7GLxMerirLbJkhbKqkSYJbMvGbDYnZvA8T1lN0+G4HlzXhWmaXGtYxhJTYbffK1uwTYNptIEfZ1gz05pOIbYtU2XlsJc+e2toU1iWhZCVhGEA13HgcJ0w66apUVUl2rZBnmcYjP76E8/++BWeH6CsG+4aYzjWMJ5M8WL4Grbn4/V4At1yUDcdqqZF0/ZouwOqusWurLDdlyo25jwGwTJD6j/D45GDz4fX+Owyx0c/WPjkJw8f/2jj058X+GJ0wINX3b/Q4iHtw6GgxQP+8+XLAoOau7xtM044wWO7w/fjDF//buObSw9f/Wbh2+c+njgtnjoNnti34PtTt30P9Hk9fjHZQ5niidPZ73bo6xIZtahNx9TjDK6ps58GDl2NN32DI+3bvlXrvqnu0DHu2DVY56kQvlFa22w21FuFMAhwNZlgMtFg2xZ7qcHgVHXD4GQNvBoOlZzqpuF0S+zLStmKPUyz7Ewo+tlut0rA8TLCZDrBlDIReZgksigjU9eh6wZl4yl51DWnSzKBxDXcIMvz+xlWinCJKQlFJqZNeTDLKTU3ox51XYO/CND1vSLouk4RvyPMz4TnIyXOPVMvihy+7yvNxZTQiloTK4iYfRAs4JJcWuO5M+Q8thX1K8hSlnw4nAnFsafi5aRIgMFSpXeqbNNgdhwQBS3+0XCE6WSMq6srHslExUqmIvR7hCUJ4yhiqRbm87k6Um3bqXLatr2zUuo7W98mIoRqKB8Qsoe6cT6rUo60Qoh3+50KFOwosTO4FvCbxKdp+g+h7CDI6LRtW8mgZ/Mlk/chWYn/wKtNrjex4hMrSrkjlIWU6ZDs4uICFvsVRSGHENxhsViogVm3t42htKnxEnGUX/r8N6RtM1wt42BHAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Create Task&quot;
        title=&quot;&quot;
        src=&quot;/static/9c94a5cb10e81d12af056f6979af1c1d/2e9f9/create_task.png&quot;
        srcset=&quot;/static/9c94a5cb10e81d12af056f6979af1c1d/69538/create_task.png 160w,
/static/9c94a5cb10e81d12af056f6979af1c1d/2e9f9/create_task.png 292w&quot;
        sizes=&quot;(max-width: 292px) 100vw, 292px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;In the &lt;strong&gt;general&lt;/strong&gt; tab, give your task appropriate name and description&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 602px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/6c260eaeca9e3a76d6ca2b5094d1fe47/32056/task_general.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 75.625%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAA7EAAAOxAGVKw4bAAACOUlEQVR42nVU2ZKjMAzk//9qH/Z9KjVVk5CbhCMknOYIGOhVi2Q2W5OlqpGxLclyt3BMZVDXNZqmQSO2bVsZt/ptrcU0TRjHUTGN83gYhh/gPH2d2+0G4ng84HCYcT6fsd1s4HkettstjmJ3ux1W7gp+4KMsS+R5/gNpmsLJsgzX6xVd12G1WqkzFy5xrPNxfHnYWMHkSZK8hQYsiwKXKELfWxSSpZBvnsDa/gH7bfuul8T3t+j7Dqaq4KRpomUWxuBrc8CXu8F67yGMU0S3dLZXsYKqtegG4G6nNwByU8NhqcNgce971LJ7lAu2dtAraO/M3is4HoSgCXgLyYO0fATkY4XBxs4O4xsHWdY9/0MvThkD1nWFKIyEWR9+FKOoGlmokBUVctlQmEZKEUl19p8Su2GSE0Nhp/mEptKANXbbnbAqbAqLSSIsUkoPhpPbVZi9om1qDEJOLwTQGiHucNjjJKrwvCMiIdaUZg4YBIHKgRIiy5RJJozrM01qnuJWgcvc/d7pPtWxSIa+FVlmQMrmfDqpoAMRLkXMMR1qdtAD7IS5kxoliVJ6fRjL4QYKkkf2/QBhGAqibz0SxlSa/YmyKGWda+ZnQCP6IylpmqmwL5eLgoko+mfgV8ugVmRGMbcvFXDN4YttwwneV6L3kWubLZdLuK6L9XoNV9rSpZXvjZB4PAfYe2ecpCrf9xU8iP4cWDJtrCz/7Us2PC+beI5LsV6U4PfnDr8+XHzuQ2zkR7JYLOQHc8QfRoB7C1khyoYAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;General&quot;
        title=&quot;&quot;
        src=&quot;/static/6c260eaeca9e3a76d6ca2b5094d1fe47/32056/task_general.png&quot;
        srcset=&quot;/static/6c260eaeca9e3a76d6ca2b5094d1fe47/69538/task_general.png 160w,
/static/6c260eaeca9e3a76d6ca2b5094d1fe47/72799/task_general.png 320w,
/static/6c260eaeca9e3a76d6ca2b5094d1fe47/32056/task_general.png 602w&quot;
        sizes=&quot;(max-width: 602px) 100vw, 602px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next, in the &lt;strong&gt;triggers&lt;/strong&gt; tab let&apos;s create a trigger for our task. Click the &lt;strong&gt;New&lt;/strong&gt; button and set up the time and repeat frequency for your task. Make sure you mark the &lt;strong&gt;Enable&lt;/strong&gt; checkbox before pressing &lt;strong&gt;OK&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 561px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f435bb79978436204849f0e805072d57/410f3/task_trigger.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 86.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAARCAYAAADdRIy+AAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB50lEQVR42p1Ta3OCMBDk//+4OnW0Vh6KGN4EAs5Y9Mv19kJoHW3r9MOSB5fN3t3GG8cPOp/PdLlcBOM4zuNPwP/r9XqHC8Pzt1taLBa02+0oDENaLpe8fqEkSUgpRcfjkdRRUapSmR8OB6qqioZhoN701PcWxhjquo68PM+FqNENtW1LeZbLgaIoqCxLntcyBznWDlme2RhGPsV2nSEPJEEQCLAJFU3TyLxwh7NMFONyUays0oSBixLeS9PUKtRa02q9ojiORXpR5DJHoFWcCenpdJK0XIpDzykP0xzp8yiE+ASBT+v1G4HcqavrWgKwbhrNZLZOv0EIDX9QB0jGYfxwN4oi81X05wgnAqSEDaSJ8b/gLmcURRGFjP1+L5v2xu+B5nlC1Cpi28CP0UQKclgJnYWFEIMsgK8LH8PDge32nXzfZ5KIu5vOTXFdRRkwommIx3/UGz7EBSjX3GUEI7CYGgOvqVTJCFI0rJxI2lbfvIzvNrpR+L7ZsHVsiniCSB1mxY3wZVlWc8pOAEaBtuNMqFl6zHXDE+t7e8CZGM8RpCBwB1zaUD9jspsQIpUwDETZ40LfN8G+FCNwKbu0PSf9r+7dGJhRaUZrqGbgJbkyeP8xb9t2FBU9reOK9uUgFntdvkojPwEcP/5YbkMz5AAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Trigger&quot;
        title=&quot;&quot;
        src=&quot;/static/f435bb79978436204849f0e805072d57/410f3/task_trigger.png&quot;
        srcset=&quot;/static/f435bb79978436204849f0e805072d57/69538/task_trigger.png 160w,
/static/f435bb79978436204849f0e805072d57/72799/task_trigger.png 320w,
/static/f435bb79978436204849f0e805072d57/410f3/task_trigger.png 561w&quot;
        sizes=&quot;(max-width: 561px) 100vw, 561px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;This step is optional if you have your software already set to start on windows startup. Create a new action by clicking &lt;strong&gt;New&lt;/strong&gt; and then add programs that you want to run when this task is launched.&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 428px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/108b4a31d1042b9fa41bda2edb05fd04/47730/task_action.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 110.00000000000001%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAB/0lEQVR42u1UyY7iUAzM/38ZEgIGDk1IICFkX8lCAHGg2vWyTDeg7pb6ModBKvwwfhW77Fi7XC44n8+4Xq/4zed+v+N2u0Er8hxhGHaIIsRRjEgsf9PGcSyIEPH/IESeF2jbFqfT6QlN00BjoGVZMLdbGIap7G63k/MGxmYD0zRhmAZ0Xcd6vcbhcEBRFMjyTMjzT6BfYyaO4yDwfSzmcyyXfzCdThWBJz5XCFzXVfA8TxHu9/snbCWRJEkkQylrsViozIIggC+XGBBIeQx4hTRNn0C/yjCX1C1r15XC1MV5Eo2oR13XgsF+RlVVCoxr+t/H41GaIgS6vsZcyl2tVgqByMBsQ6JvGCuJ+oYRzIgNoxSMTZK0y5BfQeDDkTKpJctlkwjqqEtjNgJqxAYpaxgqnufJZCK6L9U9NkYR2uyy2V2wLVvpyY5GcYK2H4emqaW0BmVZqhFpe1k4wzzT/5fQtscMODqu6ymhsyxXugyoqlJ8mdLr4+zRjoRHITyo0Tj05dpIhIzCM+gRTIB2aMoAPlBpyENXUjM+jQGvyAZ8zPoR2ivnV2TfPUj7DcF/wn+J8OGOmo7yi7H5DsMgl+Vx3DjjtvlRKYJaXjs/q/AWtpgZnrJWmMPampjNZrJAjG7B/rTMSpDkFZy0geVncJIaQVLIivNhyS7gNud7/g7gbXfpnHVvBAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Action&quot;
        title=&quot;&quot;
        src=&quot;/static/108b4a31d1042b9fa41bda2edb05fd04/47730/task_action.png&quot;
        srcset=&quot;/static/108b4a31d1042b9fa41bda2edb05fd04/69538/task_action.png 160w,
/static/108b4a31d1042b9fa41bda2edb05fd04/72799/task_action.png 320w,
/static/108b4a31d1042b9fa41bda2edb05fd04/47730/task_action.png 428w&quot;
        sizes=&quot;(max-width: 428px) 100vw, 428px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Finally, the most important step. Mark the &lt;strong&gt;&quot;Wake the computer to run this task&quot;&lt;/strong&gt; in order to allow your task to wake up your computer on its own. You can also tweak some other settings such as running the task only if the internet is available. This is obviously optional but essential if you don&apos;t want your computer to stay open doing nothing in case of no internet connectivity.&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 600px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/fcb6d8e83b9d68980eba6f2c25eef9e9/0a47e/task_conditions.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABxUlEQVR42q1Ua4+qUAzk//88EzULKqg85CEH8MFTZjtVyCbr3vjh1kxa8HROp61a18sF9f2Ouq5xp2/qOe77AeM44vF4qJ/A598Y0HUdrDAMsdu52DgOXNdVxHEM3/cRBAFs29Z39peN5XKJ/X6PPM+RZZn68/k8x2VZwsrlheNs4HkuviRpu93JsyOX7ARb7A8HScqQpqkkpkpgjAHzKlFXFAXO2RkXiZumgcUvfamEtzApTZKnFyQSl1UJ2jAMKMpCq2A7JpCEcmlK2LatBDWqqpKbc5jc4Ha74SaHr9er9KXXw33fI4oi5FIAq5lAUvaQRi6LB9lM9mDqjTGFSDEqSWYw28/4nSkhJXMIrJByiGe1xTxtSqHkf4FVqmQmbzcbneRBJkjveZ4Oxdt7OqA0SfGJaYWlNPp4PL6mmCk4Ofq2aV9Sx9n/hZkwTWKs12twH7l3JKcPghCn0wknaYfupbxjAvv9DpwFW6RT5rKuVqsnsSRymlEUKnjRhPhF/g68nMossrOhU3N/jnT+qKTxM8ks9X+ZEk4rQeJP0QmabkDd9mjFT73lD0L3cFrqT2AEUWaw8g0WXgYvKXXtFouF/qF8AxtWgPk/X+BzAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Conditions&quot;
        title=&quot;&quot;
        src=&quot;/static/fcb6d8e83b9d68980eba6f2c25eef9e9/0a47e/task_conditions.png&quot;
        srcset=&quot;/static/fcb6d8e83b9d68980eba6f2c25eef9e9/69538/task_conditions.png 160w,
/static/fcb6d8e83b9d68980eba6f2c25eef9e9/72799/task_conditions.png 320w,
/static/fcb6d8e83b9d68980eba6f2c25eef9e9/0a47e/task_conditions.png 600w&quot;
        sizes=&quot;(max-width: 600px) 100vw, 600px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Your computer is now set to wake up at your specified time. If you want to test this out, simply edit the time trigger and assign it a value close to your current time. Once created, simply sleep/hibernate your computer and wait for the magic to happen.&lt;/p&gt;
&lt;h2&gt;Step 03 : Auto Computer Hibernate/Shutdown&lt;/h2&gt;
&lt;p&gt;In the beginning of the post I said you would not need to write any code for this -- well maybe, I lied. However, I promise it&apos;s nothing more than a single line which essentially is the Windows shutdown command for shutting down your computer. So please bear with me and follow these simple steps.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Open up any text editor of your choice and place the following line into it:&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;shutdown /s&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;blockquote&gt;
&lt;p&gt;If you want to put the computer to sleep instead, simply replace the &lt;code&gt;/s&lt;/code&gt; with &lt;code&gt;/h&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Then simply save the file with whatever file name you wish to give it but be sure to give it an extension of &lt;code&gt;.bat&lt;/code&gt; e.g. &lt;code&gt;shutdown.bat&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;That&apos;s it, if you double click this file, your computer will shutdown (or sleep depending on which parameter use used).&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Step 04 : Scheduling Shutdown Task&lt;/h2&gt;
&lt;p&gt;Now, the final step is to setup the second part of scheduling i.e. turning the PC off at a specific time. To set this up, follow the &lt;a href=&quot;#step-02--scheduling-wake-up-tasks&quot;&gt;same procedure&lt;/a&gt; and specify the schedule you want your computer to shutdown/sleep. Make sure in the &lt;span class=&quot;evidence&quot;&gt;step 5&lt;/span&gt; you include the script we just created in the programs that are to be launched with this task.&lt;/p&gt;
&lt;p&gt;You&apos;re all set to test your system. However, one last step for me was to configure Internet Download Manager to automatically launch scheduled downloading task few minutes after my system wakes up. For uTorrent, only launching it was enough since it can automatically resume pending downloads.&lt;/p&gt;
&lt;h2&gt;Step 05 : Setting Up IDM For Scheduled Downloading&lt;/h2&gt;
&lt;p&gt;In order to run the scheduled downloading on IDM after your system wakes up. You have to make sure that IDM is registered either as the startup launch item or in the actions tab of your wake up task. This is to make sure that your IDM is running when it&apos;s time for the scheduled downloading to kick in. Now let&apos;s set up our scheduler.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Open &lt;strong&gt;IDM&lt;/strong&gt; and open &lt;strong&gt;scheduler&lt;/strong&gt; by clicking &lt;strong&gt;Download&lt;/strong&gt; ➜ &lt;strong&gt;Scheduler&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 264px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/f820fc32a0f4033ab2f4bf50016cfda8/e61aa/start_scheduler.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 108.74999999999999%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAWCAYAAADAQbwGAAAACXBIWXMAAA7DAAAOwwHHb6hkAAAEQklEQVR42m1Uy47jRBSNxA/xN/wBG1ZsRrMAVqCRQCMkVrCFDQhGgOhhgUb0I3R3JnEncWx3HnbsOHb8ftt5udM53Kp09/QMRDoqO646de69597GUesXKNoU87kJQzcIOj1bcOw5zJnO18lkgrlpQJ9OYds2DGOGbV3j/34NQRPghS6CwOWknutibHroqzak6QKy7qCvORjqFtTJGFEUIwwDbLdb3O732N/e4mZ3g/3+FrvdDg3PcdFut+EFOXzXgSQPIS5K/PBKxIvLCX48lul5AMXfYKZrUIYj1NsNuldX6IoS1psNhsNr9HtXaHcENNIsQ5qmqKqXKAoFSZLDDWO4UQ7LDaGZCxi2j5nlIAwC5Hl+h4KfK8qSzhUc7L0Rxysi+wRp9B6E00+RpFukSYiyyGmNYFtzLGyLckjp8H3aW9Hh/A4HInZBScRJkqDREkJ88+0HePL0fXz40XPYCx9ZliLNcsRxBFXTOJmuzw6Hi/yRyjekD4S+H+G3l2N8/vUFjpsifciIKCHSjCOOY0REnKYZyqrkCpfLJdarFVYERsr2sZXtbTgLG2K3helUQa8nUMgpv4nd6nseBKGDfl+ELEsQB1QcRcFAkqiQr6kQPYR0eXmXR64wDCPyFYVk2JhRaK7rUbjpw42GQf5j3uS5XMCyLExUlSo7hEZrkqQP4XNCjwguzv/B2fELnJ3+jatuj6vkCn0P4/GY1OvQNBUqEViWjfV6zcPlId8V5UFhUVZk6gCb4hK2qaDZbMIkpSxXzMAqdcn19TV1y5iiYEqth8q+WxRuG3ZLXW9R74AiGCFzu+S3GKuqQOg5EMmwXcqj0L6EJPbQft2i3PpYLytUVPFlWXCsqGB5mhwIb25uiLQ+5IHyZi1cOFEJm7rH9BPMg4zWFKaXwHBCWEGKRZi/gwK2H78hxH4HzSshGT7+kjx8cV7j2XGMpz/LePLTAB9/38FnRyaet2/w5cX6LXxFeHa+wXed8p5wh9tdjckshqp7NBBcnExXeDWM8GtLw9GViT+EGf7sOzjRSpzpy7fQJBxrFc6nGSNco2YKaWoMGdnEojb0sKacZHFAQ6CDoTyALIkQOm2e1+2qwqrMaU/BsVmWKLMESRQcFDIb1JsQ50MVp4MxMvqQUfOz6kuyQpbRMJqoUK5HcKkgbCBwu9yBvcdkGT8I0WAHPSqC6vwO3RKhiDKZe3HYFIUYjcbcg8zMY7IQ8xqzyH273YNZJooiNPKcqklTeThqk4E1ajWBdwz3YeDTf1P+PjNnvGPYsGDdUeT5fwjDkBSu1hvUqxwnygDi1IIsdukCi1RUiMjYvV6Xwpa4wj499/p9hDS13zU3U85S1Ch5p/jQ7SYMc0TG7cB2HB5WTCEsaHjY1MM+zUJWQJbvx2SPSVkeGyy0vKjIhh65PaDDDo2rgwKmsE2VbV22aNrIfIQ9brfHuJ+H/wKqaE41ChHugAAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Start Scheduler&quot;
        title=&quot;&quot;
        src=&quot;/static/f820fc32a0f4033ab2f4bf50016cfda8/e61aa/start_scheduler.png&quot;
        srcset=&quot;/static/f820fc32a0f4033ab2f4bf50016cfda8/69538/start_scheduler.png 160w,
/static/f820fc32a0f4033ab2f4bf50016cfda8/e61aa/start_scheduler.png 264w&quot;
        sizes=&quot;(max-width: 264px) 100vw, 264px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Next select your &lt;strong&gt;main download queue&lt;/strong&gt; or any custom queue that you want to be resumed&lt;/p&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 614px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/df9faf93f14a37fe856b8a6ca2052d78/e9131/setup_schedule.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 76.25%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAPCAYAAADkmO9VAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACFklEQVR42oVTi66iMBDl/79if2gTVIISNYCiUARfKI9COztnEPeR9d4mJ63T4fScmdE5F5mp7nfq+566rqO2bd/osHetxIHr5ULn81nwfD7/zuV7jmlnvfhhktClKNrT8Xik2+0mHxSnEyXJgQ6HA+12O4rjmHOi97koCrqzEOQDOF+vV+2swp9msXDJW3g0ny8QlNfLshQyIApDipnIGENYopZJrlDLeQ0rxF3TNNpRx43ZRaG8XhSlXDyYEK8qlYlqpRSlaSrgj8QiSPSrLFr3NJgBce1wwvjsH+vxeIhS2FYqpxPbG4aB0ZPKmDxLxeK0rLWTcu3U9UiI4GSpqiohRS1BZo3l1xuJ13XNj91kh9pea26o/k34P4UTYVkWYk9lGfm+zx9oRivnzWYrk4HcjO/hQPP6SAhMymF/u92KKv5mrDc3g2/fdrG+rOGFZy5JEulyygpQeHR3jB1lCkabrVgH8bdNgRV0GDs6jPOJG5WmGZ3ynGtuhex+r4TwYw1B+BpUGWCowUjhjDssNBB1ww4gn0vymRBzWJ5LIYV9APFX8UXRNBnAq+tf1zAIVvzvmdNsNiPXdclf+qIWj8Duv0sss3+DOgD1a8ecwQKIYRN1A2AbjRmHfFSK31pmsR8tJ8l+mLkzu15vbBAElhVZboLA8zwbRbENw9Aul0vLTeHfkeTleW5Xq8DyTNr9fo97TED3CwoBe+E4oXdZAAAAAElFTkSuQmCC&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;Setup Schedule&quot;
        title=&quot;&quot;
        src=&quot;/static/df9faf93f14a37fe856b8a6ca2052d78/e9131/setup_schedule.png&quot;
        srcset=&quot;/static/df9faf93f14a37fe856b8a6ca2052d78/69538/setup_schedule.png 160w,
/static/df9faf93f14a37fe856b8a6ca2052d78/72799/setup_schedule.png 320w,
/static/df9faf93f14a37fe856b8a6ca2052d78/e9131/setup_schedule.png 614w&quot;
        sizes=&quot;(max-width: 614px) 100vw, 614px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Then select &lt;code&gt;one-time downloading&lt;/code&gt; and specify the time you want this queue to start. You might have noticed that I&apos;ve set the time to 5 minutes after my wake-up task. Finally, you can optionally specify the repeat frequency, stop time and download complete action as well. Once done, click apply and then close.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;That was it, you have successfully build your own smart download management system that too without any coding. Enjoy your sleep while your computer does the work for you. Make sure your roommates know about this, else some poor souls are going to get spooked or you&apos;ll be accused of using the computer all night long.&lt;/p&gt;
&lt;hr&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;</content:encoded></item><item><title><![CDATA[Speed Up Your Android Development with these 10 Libraries (Part 2)]]></title><description><![CDATA[In the first part of this post{:target="_blank"}, we learnt about ButterKnife{:target="_blank"}, SDP{:target="_blank"}, Joda Time{:target…]]></description><link>https://www.zuhaibahmad.com/libraries-to-speed-up-your-android-development-part-2/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/libraries-to-speed-up-your-android-development-part-2/</guid><pubDate>Sat, 31 Dec 2016 04:42:00 GMT</pubDate><content:encoded>&lt;p&gt;In the &lt;a href=&quot;zuhaibahmad.com/10-libraries-to-speed-up-your-android-development-part-1&quot;&gt;first part of this post&lt;/a&gt;{:target=&quot;_blank&quot;}, we learnt about &lt;a href=&quot;zuhaibahmad.com/10-libraries-to-speed-up-your-android-development-part-1/#butterknife&quot;&gt;ButterKnife&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;zuhaibahmad.com/10-libraries-to-speed-up-your-android-development-part-1/#sdp&quot;&gt;SDP&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;zuhaibahmad.com/10-libraries-to-speed-up-your-android-development-part-1/#joda-time&quot;&gt;Joda Time&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;zuhaibahmad.com/10-libraries-to-speed-up-your-android-development-part-1/#activeandroid&quot;&gt;ActiveAndroid&lt;/a&gt;{:target=&quot;_blank&quot;} and &lt;a href=&quot;zuhaibahmad.com/10-libraries-to-speed-up-your-android-development-part-1/#eventbus&quot;&gt;EventBus&lt;/a&gt;{:target=&quot;_blank&quot;} libraries to help us speed up our Android development. Now, let&apos;s continue from where we left off and explore the remaining libraries in our list.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#volley&quot;&gt;Volley&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#okhttp&quot;&gt;Okhttp&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#parceler&quot;&gt;Parceler&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#android-annotations&quot;&gt;Android Annotations&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#espresso&quot;&gt;Espresso&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Volley&lt;/h2&gt;
&lt;p&gt;Network operations have always been a frustrating task for developers mainly due to the excessive amount of tedious work involved. Without the help of any networking library, to read from even the most basic REST APIs that spits data in &lt;code&gt;JSON&lt;/code&gt; format, you will need to setup an &lt;code&gt;HttpURLConnection&lt;/code&gt;, &lt;code&gt;InputStream&lt;/code&gt;/&lt;code&gt;OutputStream&lt;/code&gt;, a &lt;em&gt;JSON parser&lt;/em&gt; and you will also have to place all these tasks into an &lt;code&gt;AsyncTask&lt;/code&gt; to separate them from UI thread and run asynchronously. That&apos;s a lot of work with a lot of boilerplate code, probably more than anything else considering the work we are getting done for the amount of code that we write.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://developer.android.com/training/volley/index.html&quot;&gt;Volley&lt;/a&gt;{:target=&quot;_blank&quot;} is a HTTP networking library by Google that makes networking tasks easier, helps you get rid of boilerplate and most importantly, it&apos;s faster. Some of the key benefits of Volley are:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Automatic scheduling of network requests.&lt;/li&gt;
&lt;li&gt;Multiple concurrent network connections.&lt;/li&gt;
&lt;li&gt;Support for request prioritization.&lt;/li&gt;
&lt;li&gt;Highly flexible and easily customizable.&lt;/li&gt;
&lt;li&gt;Comes with debugging and tracing tools.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, Google advices not to use Volley for large download or streaming operations, since Volley holds all responses in memory during parsing. Another limitation is that the only response types supported are &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Image&lt;/code&gt;, &lt;code&gt;JSONObject&lt;/code&gt;, and &lt;code&gt;JSONArray&lt;/code&gt; also note that there is also no built-in &lt;code&gt;XML&lt;/code&gt; support, but due to Volley&apos;s flexibility you can build one yourself.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Setup for Volley is fairly simple, just add this line to your &lt;span class=&quot;evidence&quot;&gt;app/build.gradle&lt;/span&gt; file and you are good to go:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;compile &amp;#39;com.android.volley:volley:1.0.0&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;Make sure you have &lt;code&gt;android.permission.INTERNET&lt;/code&gt; permission in your manifest file.
Create a &lt;code&gt;RequestQueue&lt;/code&gt; using Volley&apos;s convenience method &lt;code&gt;Volley.newRequestQueue&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
RequestQueue queue = Volley.newRequestQueue(context);&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Specify the URL for request you are making:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;final String URL =&amp;quot;http://www.mywebservice.com&amp;quot;;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Create a Volley Request.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;There are plenty of available request types such as &lt;code&gt;StringRequest&lt;/code&gt;, &lt;code&gt;JsonObjectRequest&lt;/code&gt;, &lt;code&gt;JsonArrayRequest&lt;/code&gt; as well as your custom defined request types.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
StringRequest request = new StringRequest(Request.Method.GET, url,
new Response.Listener&amp;lt;String&amp;gt;() {
@Override
public void onResponse(String response) {
// Display first 50 characters of the response string.
Toast.makeText(context, &amp;quot;Response is: &amp;quot;+
response.substring(0,50), Toast.LENGTH_LONG).show();
}
}, new Response.ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
// In case of error
Toast.makeText(context, &amp;quot;Error in response!&amp;quot;,
Toast.LENGTH_LONG).show();
}
});&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Finally, add this request to your Queue:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;queue.add(request);&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Although this eliminates a lot of excessive work but still involves a fair amount of code and also it isn&apos;t very clean. Nonetheless, it gets the job done, it&apos;s robust and very useful for small to medium scale projects.&lt;/p&gt;
&lt;h2&gt;Okhttp&lt;/h2&gt;
&lt;p&gt;Volley is a fairly easy to use and a complete package which provides support ranging from basic String response to Image loading. However, there are some problems. Quoting &lt;a href=&quot;http://stackoverflow.com/questions/16902716/comparison-of-android-networking-libraries-okhttp-retrofit-volley&quot;&gt;this&lt;/a&gt;{:target=&quot;_blank&quot;} answer on &lt;a href=&quot;http://stackoverflow.com/&quot;&gt;Stackoverflow&lt;/a&gt;{:target=&quot;_blank&quot;} by &lt;a href=&quot;http://stackoverflow.com/users/115145/commonsware&quot;&gt;CommonsWare&lt;/a&gt;{:target=&quot;_blank&quot;}:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;it is an unsupported, &quot;throw the code over the wall and do an I|O presentation on it&quot; library.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;which is to say that all it does is &lt;strong&gt;&quot;getting the job done&quot;&lt;/strong&gt;. Which comes at a price of &lt;em&gt;not-so-good-looking&lt;/em&gt; code, lack of multipart requests and most of the load being put on the device memory.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;square.github.io/okhttp/&quot;&gt;OkHttp&lt;/a&gt;{:target=&quot;_blank&quot;} is a networking library developed by Square for sending and receiving HTTP-based network requests. It is built on top of the &lt;a href=&quot;https://github.com/square/okio&quot;&gt;Okio&lt;/a&gt;{:target=&quot;_blank&quot;} library, which tries to be more efficient about reading and writing data than the standard Java I/O libraries by creating a shared memory pool. It also is the underlying library for &lt;a href=&quot;https://square.github.io/retrofit/&quot;&gt;Retrofit&lt;/a&gt;{:target=&quot;_blank&quot;} library that provides type safety for consuming REST-based APIs.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Make sure you also check out &lt;a href=&quot;https://square.github.io/retrofit/&quot;&gt;Retrofit&lt;/a&gt;{:target=&quot;_blank&quot;} which is another awesome library by &lt;a href=&quot;http://square.github.io/&quot;&gt;Square&lt;/a&gt;{:target=&quot;_blank&quot;} which competes with Volley. It is a Type-safe HTTP client for Android and Java that turns your HTTP API into a Java interface.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Add this line to your &lt;span class=&quot;evidence&quot;&gt;app/build.gradle&lt;/span&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
compile &amp;#39;com.squareup.okhttp3:okhttp:3.3.0&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;Again, first step is to make sure you have &lt;code&gt;android.permission.INTERNET&lt;/code&gt; permission in your manifest file.&lt;/p&gt;
&lt;p&gt;Specify the URL for request you are making:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;final String URL =&amp;quot;http://www.mywebservice.com&amp;quot;;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Instantiate an &lt;code&gt;OkHttpClient&lt;/code&gt; and create a &lt;code&gt;Request&lt;/code&gt; object:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
OkHttpClient client = new OkHttpClient();
Request request = new Request.Builder().url(URL).build();&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;To make a synchronous network call, use the &lt;code&gt;Client&lt;/code&gt; to create a &lt;code&gt;Call&lt;/code&gt; object and use the &lt;code&gt;execute&lt;/code&gt; method.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;Response response = client.newCall(request).execute();&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;To make asynchronous calls, also create a &lt;code&gt;Call&lt;/code&gt; object but use the &lt;code&gt;enqueue&lt;/code&gt; method.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
client.newCall(request).enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
// Do your exception handling here
e.printStackTrace();
}

    @Override
    public void onResponse(Call call, final Response response)
        throws IOException {
        if (!response.isSuccessful()) {
            throw new IOException(&amp;quot;Unexpected code &amp;quot; + response);
        } else {
            String text = response.body().string();
            // Display first 50 characters of the response string.
            Toast.makeText(context, &amp;quot;Response is: &amp;quot;+
                text.substring(0,50), Toast.LENGTH_LONG).show();
        }
    }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;Parceler&lt;/h2&gt;
&lt;p&gt;Now, that we are done with networking, let&apos;s take a look at another headache - creating &lt;code&gt;Parcelable&lt;/code&gt; objects. Although creating &lt;code&gt;Parcelable&lt;/code&gt; s is faster than using &lt;code&gt;Serializable&lt;/code&gt; s, creating &lt;code&gt;Parcelable&lt;/code&gt; objects requires creating a lot of boilerplate code in defining data exactly in the sequence that should be serialized and deserialized.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://parceler.org/&quot;&gt;Parceler&lt;/a&gt;{:target=&quot;_blank&quot;} helps you automate this task. Similar to &lt;a href=&quot;http://jakewharton.github.io/butterknife/&quot;&gt;Butterknife&lt;/a&gt;{:target=&quot;_blank&quot;} this library generates the necessary wrapper classes for you at compile time automatically, saving you the repetitive steps required for leveraging the performance benefits of Parcelables.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Using Gradle, add this block to &lt;span class=&quot;evidence&quot;&gt;build.gradle&lt;/span&gt; at the project-level, it allows code generation libraries like Butterknife and Parceler for annotation processing:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;dependencies {
classpath &amp;#39;com.neenbedankt.gradle.plugins:android-apt:1.8&amp;#39;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And then in your &lt;span class=&quot;evidence&quot;&gt;app/build.gradle&lt;/span&gt; file, add the following:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
apply plugin: &amp;#39;com.neenbedankt.android-apt&amp;#39;

...

dependencies {
compile &amp;#39;org.parceler:parceler-api:1.1.1&amp;#39;
apt &amp;#39;org.parceler:parceler:1.1.1&amp;#39;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;By using &lt;a href=&quot;https://parceler.org/&quot;&gt;Parceler&lt;/a&gt;{:target=&quot;_blank&quot;} you no longer have to implement the &lt;code&gt;Parcelable&lt;/code&gt; interface, the &lt;code&gt;writeToParcel()&lt;/code&gt; or &lt;code&gt;createFromParcel()&lt;/code&gt; or the &lt;code&gt;public static final CREATOR&lt;/code&gt;. You simply annotate a POJO with &lt;code&gt;@Parcel&lt;/code&gt; and Parceler does the rest.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;@Parcel
public class User {
String name;
int age;

    // Empty constructor required by the Parceler library
    public User() {}

    public User(int age, String name) {
        this.age = age;
        this.name = name;
    }

    public String getName() { return name; }

    public int getAge() { return age; }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;To generated Parcelable from POJO, use the Parcels utility class:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
Parcelable wrapped = Parcels.wrap(new Example(&amp;quot;John Doe&amp;quot;, 22));&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;To create your POJO from generated &lt;code&gt;Parcelable&lt;/code&gt;&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;User user = Parcels.unwrap(wrapped);
user.getName(); // John Doe
user.getAge(); // 22&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;blockquote&gt;
&lt;p&gt;There is an Android Studio Plug-in which makes it even easier to generate &lt;code&gt;Parcelable&lt;/code&gt;s. We will be looking at it in a future post dedicated to Android Studio Plug-ins.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;Android Annotations&lt;/h2&gt;
&lt;p&gt;Android Annotations is a framework that speeds up Android development by replacing most of the repetitive tasks and patterns with java annotations. It takes care of the plumbing, and lets you concentrate on what&apos;s really important. By simplifying your code, it facilitates its maintenance.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Using Gradle, add this block to &lt;span class=&quot;evidence&quot;&gt;build.gradle&lt;/span&gt; at the project-level:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath &amp;#39;com.neenbedankt.gradle.plugins:android-apt:1.8&amp;#39;
}
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And then in your &lt;span class=&quot;evidence&quot;&gt;app/build.gradle&lt;/span&gt; file, add the following:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;apply plugin: &amp;#39;android-apt&amp;#39;

...

dependencies {
apt &amp;quot;org.androidannotations:androidannotations:4.2.0&amp;quot;
compile &amp;quot;org.androidannotations:androidannotations-api:4.2.0&amp;quot;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;Following is an example of a simple &lt;code&gt;MainActivity&lt;/code&gt; when used without Android Annotations library and when used with it:&lt;/p&gt;
&lt;h4&gt;Before&lt;/h4&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(FLAG_FULLSCREEN, FLAG_FULLSCREEN);

        setContentView(R.layout.activity_main);

        TextView msgTextView = (TextView) findViewById(R.id.tv_msg);
        Button hideButton = (Button) findViewById(R.id.btn_hide);
        hideButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                msgTextView.setVisibility(View.INVISIBLE);
            }
        });
    }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h4&gt;After&lt;/h4&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;@Fullscreen
@EActivity(R.layout.activity_main)
@WindowFeature(Window.FEATURE_NO_TITLE)
public class MainActivity extends AppCompatActivity {

    @ViewById(R.id.tv_msg)
    TextView mMsgTextView;

    @Click(R.id.btn_hide)
    protected void onHideButtonClick() {
        mMsgTextView.setVisibility(View.INVISIBLE);
    }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;You can see how clean, concise and readable the code has become after using annotations. There&apos;s a lot more to this brilliant library, head over to &lt;a href=&quot;https://github.com/androidannotations/androidannotations/wiki&quot;&gt;official documentation&lt;/a&gt;{:target=&quot;_blank&quot;} to find out more features.&lt;/p&gt;
&lt;h2&gt;Espresso&lt;/h2&gt;
&lt;p&gt;So far we have covered different aspects of our development routine and tried to simplify them with the use of 3rd party libraries. This seems to increase overall performance and productivity in our projects but in the end if you cannot test it, it&apos;s probably not worth it. &lt;a href=&quot;&quot;&gt;Testing&lt;/a&gt;{:target=&quot;_blank&quot;} has been an integral part of Software Engineering and Android development is no different. In your early days of Android development, you might be testing your applications manually by test driving them every time you added or fixed something. Many might still be doing the same if you have not switched to automated tools yet.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;&quot;&gt;Espresso&lt;/a&gt;{:target=&quot;_blank&quot;} is part of &lt;a href=&quot;&quot;&gt;Android Testing Support Library&lt;/a&gt;{:target=&quot;_blank&quot;} provided by &lt;a href=&quot;&quot;&gt;Google&lt;/a&gt;{:target=&quot;_blank&quot;}. The basic idea is to get rid of manual testing and let the system do the UI testing for you in matter of seconds. You write intuitive and easy to read tests in the form of each individual action you have to perform in order to get the task done and then finally evaluate the results. All the operations in Espresso follow this simple pattern&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Find A View&lt;/strong&gt; ➜ &lt;strong&gt;Perform Some Action(s)&lt;/strong&gt; ➜ &lt;strong&gt;Validate Output&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Each of the steps in your test path are more or less exactly written step by step in Espresso and even a person with little or no knowledge about the domain or your project can easily start writing tests for your application. This ease of use makes it one of the best (if not &lt;strong&gt;&lt;em&gt;the best&lt;/em&gt;&lt;/strong&gt;) UI testing framework available for Android.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;In the &lt;span class=&quot;evidence&quot;&gt;build.gradle&lt;/span&gt; file of your Android app module, you must set a dependency reference to the Espresso library:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
androidTestCompile
&amp;#39;com.android.support.test.espresso:espresso-core:2.2.2&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;If you are to test the Login screen of your application, you might have to first input email and password in the corresponding fields and then click the Sign In/Log In button. These are 3 separate operations, additionally you might want to first check if Login Form itself is visible. Finally you will check if the application displays a dialog indicating login success in case of successful Login. Let&apos;s see how to do this using Espresso.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;@RunWith(AndroidJUnit4.class)
public class LoginActivityTest {

    @Test
    public void onLoginSuccess_displaysLoginSuccessDialog() {
    	//Declare our email and password
    	String username = &amp;quot;myusername@gmail.com&amp;quot;;
    	String password = &amp;quot;mypassword&amp;quot;;

    	// Login success dialog&amp;#39;s message
    	String successMsg = &amp;quot;Login Successful&amp;quot;;

        // Check if Login form is visible
        onView(withId(R.id.LLLogin)).check(matches(isDisplayed()));
        // Fill details in form
        onView(withId(R.id.ETUsername)).perform(typeText(username));
        onView(withId(R.id.ETPassword)).perform(typeText(password));
        // Click on Login button
        onView(withId(R.id.BTLogin)).perform(click());
        // Check if Login Success dialog is displayed
        onView(withText(successMsg)).check(matches(isDisplayed()));
    }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;As you can see, this is just an &lt;em&gt;easy to read and easy to write&lt;/em&gt; code representation of a real user&apos;s actions. You can create all sorts of complex tests by chaining these simple methods together. There are many different types of &lt;code&gt;actions&lt;/code&gt;, &lt;code&gt;matchers&lt;/code&gt; and other utilities available in &lt;a href=&quot;&quot;&gt;Espresso API&lt;/a&gt;{:target=&quot;_blank&quot;}, to learn more about them, head over to the official &lt;a href=&quot;&quot;&gt;Espresso Documentation&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Conclusion&lt;/h2&gt;
&lt;p&gt;So these were the ten must have libraries in my opinion that every Android developer must start using in order to make their code:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Easy to write&lt;/li&gt;
&lt;li&gt;Easy to test&lt;/li&gt;
&lt;li&gt;Easy to maintain&lt;/li&gt;
&lt;li&gt;Concise and Performant&lt;/li&gt;
&lt;li&gt;Highly cohesive and low coupled&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;and finally improving your overall development speed and productivity, because in the end being a good developer is about &lt;em&gt;working smart, not hard&lt;/em&gt;. I hope you have found this article useful. Feel free to provide your valuable feedback.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;sub&gt;Most of the example code and definitions were adopted from official sources to avoid any errors.&lt;/sub&gt;&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;</content:encoded></item><item><title><![CDATA[Speed Up Your Android Development with these 10 Libraries (Part 1)]]></title><description><![CDATA[Introduction An average developer writes somewhere between few dozen to a couple of hundred lines of code per day. If you are an Android…]]></description><link>https://www.zuhaibahmad.com/libraries-to-speed-up-your-android-development-part-1/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/libraries-to-speed-up-your-android-development-part-1/</guid><pubDate>Sat, 17 Dec 2016 01:08:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;An average developer writes somewhere between few dozen to a couple of hundred lines of code per day. If you are an Android developer, you might be closer to the upper limit. Reason? &lt;a href=&quot;https://en.wikipedia.org/wiki/Boilerplate_code&quot;&gt;Boilerplate&lt;/a&gt;{:target=&quot;_blank&quot;}.. &lt;strong&gt;A LOT OF BOILERPLATE!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;From &lt;code&gt;findViewById()&lt;/code&gt; to writing &lt;code&gt;Adapters&lt;/code&gt; and &lt;code&gt;Interfaces&lt;/code&gt; for different types of operation, we write loads of boilerplate everyday (&lt;em&gt;at least we juniors do!&lt;/em&gt;). Some people still seems surprised about the &lt;strong&gt;&lt;em&gt;&quot;10 Lines per Developer per Day&quot;&lt;/em&gt;&lt;/strong&gt; statement from &lt;a href=&quot;https://www.amazon.com/Mythical-Man-Month-Software-Engineering-Anniversary/dp/0201835959&quot;&gt;The Mythical Man-Month&lt;/a&gt;{:target=&quot;_blank&quot;} &lt;em&gt;(a must read book for any software engineer)&lt;/em&gt; and some even proudly say that they could beat it by huge margin &lt;em&gt;(impressive!!)&lt;/em&gt;. What many seems to neglect is the importance of &lt;em&gt;negative code count&lt;/em&gt;. You gain more advantage by removing &lt;em&gt;(refactoring)&lt;/em&gt; code than by adding new.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If we wish to count lines of code, we should not regard them as &quot;lines produced&quot; but as &quot;lines spent&quot;. - Edsger Dijkstra&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Now if you apply this theory to Android development, this makes a big difference. What if you were able to remove even half of the boilerplate code from your projects? You might be able to achieve 2x development speed to say the least.&lt;/p&gt;
&lt;p&gt;Android community is enormous and they keep improving the eco-system with latest libraries and methodologies. There are actually plenty of libraries already written to boost the development speed. In this post, we will discuss my list of 10 &lt;em&gt;must have&lt;/em&gt; libraries that can boost your Android development.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#butterknife&quot;&gt;ButterKnife&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#sdp&quot;&gt;SDP&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#joda-time&quot;&gt;Joda Time&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#activeandroid&quot;&gt;ActiveAndroid&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#eventbus&quot;&gt;EventBus&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;ButterKnife&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;findViewById()&lt;/code&gt; &lt;br/&gt; &lt;code&gt;setOnClickListener(this)&lt;/code&gt; &lt;br/&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;How many times do you have to write this code in a single project? A year ago, a single &lt;code&gt;Activity&lt;/code&gt; (with a complex UI) in one of my projects had about 150 lines of only this code (along with some default value settings). Out of embarrassment, I decided to move this code out from &lt;code&gt;onCreate&lt;/code&gt; method to into its own method &lt;code&gt;setComponents()&lt;/code&gt; and hide it somewhere near the end of the class. Why? to make it look.. well.. &lt;em&gt;less embarrassing&lt;/em&gt; *cough*&lt;/p&gt;
&lt;p&gt;Few months back, I got to work on that project again. First of all, I took a fair share of time to laugh at that mess. I ended up replacing most of the UI related boilerplate with &lt;a href=&quot;http://jakewharton.github.io/butterknife/&quot;&gt;ButterKnife&lt;/a&gt;{:target=&quot;_blank&quot;} and the code count of that particular &lt;code&gt;Activity&lt;/code&gt; came down to somewhere around 900 from 1700+. Image what affect this small changed made to the entire project.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Butterknife&lt;/strong&gt; is a View Injection library that uses &lt;code&gt;annotations&lt;/code&gt; to automatically generate common boilerplate code and that too in its own auto-generated classes. This not only saves you a lot of time but also makes your code cleaner and concise.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Using Gradle, add this block to &lt;span class=&quot;evidence&quot;&gt;build.gradle&lt;/span&gt; at the project-level:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath &amp;#39;com.neenbedankt.gradle.plugins:android-apt:1.8&amp;#39;
}
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;And then in your &lt;span class=&quot;evidence&quot;&gt;app/build.gradle&lt;/span&gt; file, add the following:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
apply plugin: &amp;#39;android-apt&amp;#39;

...

dependencies {
compile &amp;#39;com.jakewharton:butterknife:8.4.0&amp;#39;
apt &amp;#39;com.jakewharton:butterknife-compiler:8.4.0&amp;#39;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;Eliminate &lt;code&gt;findViewById&lt;/code&gt; with &lt;code&gt;@BindView&lt;/code&gt; on field declaration:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;class ExampleActivity extends Activity {
// Automatically finds each field by the specified ID.
@BindView(R.id.title) TextView title;

@Override public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_activity);
ButterKnife.bind(this);
// Start using your fields...
}
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Improved Listeners with &lt;code&gt;annotations&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
@OnClick(R.id.submit)
public void sayHi(Button button) {
button.setText(&amp;quot;Hello!&amp;quot;);
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;You can even eliminate resource lookups with resource &lt;code&gt;annotations&lt;/code&gt;:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;class ExampleActivity extends Activity {
@BindString(R.string.title) String title;
@BindDrawable(R.drawable.graphic) Drawable graphic;
@BindColor(R.color.red) int red;
@BindDimen(R.dimen.spacer) Float spacer;
// ...
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;This is just the tip of an iceberg, there is a lot more to this library. To learn more, head over to the library&apos;s page on creator @JakeWharton &apos;s website.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;BONUS:&lt;/strong&gt; There is even an &lt;code&gt;Android Studio&lt;/code&gt; plug-in for &lt;code&gt;Butterknife&lt;/code&gt; called &lt;a href=&quot;https://github.com/avast/android-butterknife-zelezny&quot;&gt;ButterKnifeZelezny&lt;/a&gt;{:target=&quot;_blank&quot;}, it automates the binding process with a few clicks, making things lightning fast!&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;SDP&lt;/h2&gt;
&lt;p&gt;No # 2 on my list is a real underdog but a true life-saver! Even if you have only written a basic &lt;a href=&quot;https://en.wikipedia.org/wiki/%22Hello,_World!%22_program&quot;&gt;Hello World&lt;/a&gt;{:target=&quot;_blank&quot;} program so far. You might be well aware of the problems with supporting vast variety of screen sizes associated with Android devices. No matter how many variations you create for your layout, there will always be some devices that will completely destroy your layout.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/intuit/sdp&quot;&gt;SDP&lt;/a&gt;{:target=&quot;_blank&quot;} resolves this problem to some extent by providing a new size unit named &lt;code&gt;sdp&lt;/code&gt; (scalable dp). This size unit scales with the screen size hence leading to adaptive layouts. Although this approach is not entirely foolproof and most experienced developers might insist on avoiding it, still I found it very helpful and reliable so far.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Add the following dependency in your &lt;span class=&quot;evidence&quot;&gt;app/build.gradle&lt;/span&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
dependencies {
compile &amp;#39;com.intuit.sdp:sdp-android:1.0.4&amp;#39;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;Not much changes with this library. You only need to develop one layout variation for phones and one for tablets &lt;em&gt;(yes, just two!)&lt;/em&gt;. The process is exactly the same as your normal layout file, the only difference is that you replace &lt;code&gt;dp&lt;/code&gt; with &lt;code&gt;sdp&lt;/code&gt; resource values provided by the library in your layout.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;lt;View
android:layout_width=&amp;quot;@dimen/\_24sdp&amp;quot;
android:layout_height=&amp;quot;wrap_content&amp;quot;
...
/&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;To learn more about usage and sample layout, go the project&apos;s &lt;a href=&quot;https://github.com/intuit/sdp&quot;&gt;GitHub repository&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;
&lt;h2&gt;Joda Time&lt;/h2&gt;
&lt;p&gt;Almost every project require some level of &lt;code&gt;date&lt;/code&gt; and &lt;code&gt;time&lt;/code&gt; manipulation. The standard date and time classes prior to &lt;code&gt;Java SE 8&lt;/code&gt; are poorly written and broken to some extent. &lt;a href=&quot;http://www.joda.org/joda-time/&quot;&gt;Joda-Time&lt;/a&gt;{:target=&quot;_blank&quot;} became the de facto standard date and time library for Java due to its extra ordinary capabilities. The design allows for multiple calendar systems &lt;em&gt;(such as Gregorian, Julian, Buddhist, Coptic, Ethiopic and Islamic calendar systems)&lt;/em&gt;, while still providing a simple API. There is a lot more to it which you can find out at project&apos;s &lt;a href=&quot;https://github.com/JodaOrg/joda-time&quot;&gt;GitHub repository&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Add the following dependency in your &lt;span class=&quot;evidence&quot;&gt;app/build.gradle&lt;/span&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
dependencies {
compile &amp;#39;joda-time:joda-time:2.9.6&amp;#39;
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;public boolean isAfterPayDay(DateTime datetime) {
if (datetime.getMonthOfYear() == 2) { // February is month 2!!
return datetime.getDayOfMonth() &amp;gt; 26;
}
return datetime.getDayOfMonth() &amp;gt; 28;
}

public Days daysToNewYear(LocalDate fromDate) {
LocalDate newYear = fromDate.plusYears(1).withDayOfYear(1);
return Days.daysBetween(fromDate, newYear);
}

public boolean isRentalOverdue(DateTime datetimeRented) {
Period rentalPeriod = new Period().withDays(2).withHours(12);
return datetimeRented.plus(rentalPeriod).isBeforeNow();
}

public String getBirthMonthText(LocalDate dateOfBirth) {
return dateOfBirth.monthOfYear().getAsText(Locale.ENGLISH);
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h2&gt;ActiveAndroid&lt;/h2&gt;
&lt;p&gt;One of the most painful tasks in Android development is setting up an &lt;code&gt;SQLite&lt;/code&gt; &lt;code&gt;Database&lt;/code&gt;. With all the &lt;code&gt;Keys&lt;/code&gt;, &lt;code&gt;Tables&lt;/code&gt;, &lt;code&gt;Cursors&lt;/code&gt;, &lt;code&gt;Helper&lt;/code&gt; and what not, it usually takes an entire package full of classes (filled with boilerplate code for the most part) to be able to produce even the most simplest Databases. ActiveAndroid takes care of all the setup and messy stuff, and all with just a few simple steps of configuration.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So what exactly is ActiveAndroid?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;ActiveAndroid is an active record style &lt;a href=&quot;https://en.wikipedia.org/wiki/Object-relational_mapping&quot;&gt;ORM&lt;/a&gt;{:target=&quot;_blank&quot;} (object relational mapper). Which allows you to save and retrieve SQLite database records without ever writing a single SQL statement. Each database record is wrapped neatly into a class with methods like save() and delete().&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Add the following dependency in your &lt;span class=&quot;evidence&quot;&gt;app/build.gradle&lt;/span&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
repositories {
mavenCentral()
maven {
url &amp;quot;https://oss.sonatype.org/content/repositories/snapshots/&amp;quot;
}
}

compile &amp;#39;com.michaelpardo:activeandroid:3.1.0-SNAPSHOT&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Next, configure your project by adding some global settings in your &lt;span class=&quot;evidence&quot;&gt;AndroidManifest.xml&lt;/span&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&amp;lt;manifest ...&amp;gt;
&amp;lt;application android:name=&amp;quot;com.activeandroid.app.Application&amp;quot;
...&amp;gt;

        ...

        &amp;lt;!--Database name--&amp;gt;
        &amp;lt;meta-data android:name=&amp;quot;AA_DB_NAME&amp;quot;
          android:value=&amp;quot;Pickrand.db&amp;quot; /&amp;gt;
        &amp;lt;!--Database version (default should be 1)--&amp;gt;
        &amp;lt;meta-data android:name=&amp;quot;AA_DB_VERSION&amp;quot;
          android:value=&amp;quot;5&amp;quot; /&amp;gt;

    &amp;lt;/application&amp;gt;

&amp;lt;/manifest&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Notice also that the application name points to the ActiveAndroid application class. If you are using a custom &lt;code&gt;Application&lt;/code&gt; class, just extend com.activeandroid.app.Application instead of android.app.Application.&lt;/p&gt;
&lt;p&gt;Even if you are already extending &lt;code&gt;Application&lt;/code&gt; class from another library, you can initialize ActiveAndroid in its &lt;code&gt;onCreate&lt;/code&gt; method:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public class MyApplication extends SomeLibraryApplication {
@Override
public void onCreate() {
super.onCreate();
ActiveAndroid.initialize(this);
}
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;ActiveAndroid is a very powerful library, and thus its setup process takes a bit longer than others we have seen so far. You might run into few issues in your first attempt as well. For troubleshooting and guides, go to the official &lt;a href=&quot;activeandroid.com&quot;&gt;documentation&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;Usage of ActiveAndroid is a very broad topic and even a post dedicated to it will not be sufficient to cover all the features. So we leave this to the official &lt;a href=&quot;https://github.com/pardom/ActiveAndroid/wiki/Getting-started&quot;&gt;wiki&lt;/a&gt;{:target=&quot;_blank&quot;} where you can learn almost everything about it.&lt;/p&gt;
&lt;h2&gt;EventBus&lt;/h2&gt;
&lt;p&gt;You might be used to writing &lt;code&gt;Interfaces&lt;/code&gt; for communication between your Activities and Fragments as well as different modules of your project. Now this is a pretty standard way of doing things but as your projects starts to grow, things may get out of hands.
As a result, developers often end up with &lt;a href=&quot;http://stackoverflow.com/questions/2832017/what-is-the-difference-between-loose-coupling-and-tight-coupling-in-object-orien&quot;&gt;tightly coupling&lt;/a&gt;{:target=&quot;_blank&quot;} the components of their Application. This results in a code that is less maintainable and hard to test.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://greenrobot.org/eventbus/&quot;&gt;EventBus&lt;/a&gt;{:target=&quot;_blank&quot;} is a popular open-source library that was created to solve this problem using the &lt;a href=&quot;https://en.wikipedia.org/wiki/Publish%E2%80%93subscribe_pattern&quot;&gt;publisher/subscriber pattern&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;
&lt;p&gt;EventBus enables central communication to decoupled classes with just a few lines of code resulting in simplification of code, low coupling, &lt;a href=&quot;http://stackoverflow.com/a/10830225&quot;&gt;high cohesion&lt;/a&gt;{:target=&quot;_blank&quot;}, and ultimately speeding up your development.&lt;/p&gt;
&lt;h3&gt;Setup&lt;/h3&gt;
&lt;p&gt;Add the following dependency in your &lt;span class=&quot;evidence&quot;&gt;app/build.gradle&lt;/span&gt; file:&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;compile &amp;#39;org.greenrobot:eventbus:3.0.0&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Usage&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Define events&lt;/strong&gt;: Events are POJO (plain old Java object) without any specific requirements.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
public class LoginEvent {

    public final boolean status;

    public LoginEvent(boolean status) {
        this.status = status;
    }

    public boolean getStatus(){
      return status;
    }

}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;strong&gt;Prepare subscribers&lt;/strong&gt;: Subscribers implement event handling methods that will be called when an event is posted. These are defined with the &lt;code&gt;@Subscribe&lt;/code&gt; annotation.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;// This method will be called when a LoginEvent is posted
@Subscribe
public void handleLoginEvent(LoginEvent event) {
boolean status = event.getStatus();
if(status) // Show welcome message if status is true
showWelcomeMessage();
else // Otherwise show login fail message
showLoginFailedMessage();
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;Subscribers also need to register themselves to and unregister from the bus.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
@Override
public void onStart() {
super.onStart();
// Register subscribers on activity start
EventBus.getDefault().register(this);
}

@Override
public void onStop() {
// Unregister subscribers on activity stop
EventBus.getDefault().unregister(this);
super.onStop();
}&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;p&gt;&lt;strong&gt;Post Events&lt;/strong&gt;: Post an event from any part of your code. All currently registered subscribers matching the event type will receive it.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;EventBus.getDefault().post(new LoginEvent(isLogin));&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;hr&gt;
&lt;p&gt;These were the first five from the list of libraries to help us speed up our Android development. Find out about the remaining five in the &lt;a href=&quot;zuhaibahmad.com/2016-12-18-10-libraries-to-speed-up-your-android-development-part-2&quot;&gt;second part&lt;/a&gt;{:target=&quot;_blank&quot;} of this post.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;sub&gt;Most of the example code and definitions were adopted from official sources to avoid any errors.&lt;/sub&gt;&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;</content:encoded></item><item><title><![CDATA[Build Your Personal Websites with Jekyll & GitHub Pages]]></title><description><![CDATA[Introduction Having a personal website is a trend these days. It also becomes an important tool if you belong to a profession that requires…]]></description><link>https://www.zuhaibahmad.com/build-website-with-jekyll-and-github-pages/</link><guid isPermaLink="false">https://www.zuhaibahmad.com/build-website-with-jekyll-and-github-pages/</guid><pubDate>Mon, 12 Dec 2016 02:04:00 GMT</pubDate><content:encoded>&lt;h2&gt;Introduction&lt;/h2&gt;
&lt;p&gt;Having a personal website is a trend these days. It also becomes an important tool if you belong to a profession that requires a showcase for your portfolio items e.g Designer, Developer, Artist etc. Even if you are not one of them, you might still want to spread your ideas through a personal blog. However, most of us never bother trying to get one. Mainly due to one of the following reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Hosting cost a few bucks&lt;/strong&gt; and paying for a &lt;em&gt;&apos;not-so-necessary&apos;&lt;/em&gt; thing might feel like an overkill to you.&lt;/li&gt;
&lt;li&gt;Or may be, you &lt;strong&gt;don&apos;t know how to build a website&lt;/strong&gt; (and don&apos;t want to pay someone for it).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Well, seems like those are the days of past now. In this post, we&apos;ll learn how to setup a fully featured website without any cost using &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;{:target=&quot;_blank&quot;} and &lt;a href=&quot;https://jekyllrb.com&quot;&gt;Jekyll&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;
&lt;hr&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#getting-started-with-github-pages&quot;&gt;Getting Started with GitHub Pages&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#what-is-github-pages&quot;&gt;What is GitHub Pages&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#setting-up-github-repo&quot;&gt;Setting up GitHub repo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#setting-up-your-blogwebsite&quot;&gt;Setting Up Your Blog/Website&lt;/a&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;#what-is-jekyll&quot;&gt;What is Jekyll&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#give-your-site-personal-touch-with-themes&quot;&gt;Give Your Site Personal Touch With Themes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#customizing--testing-your-website&quot;&gt;Customizing &amp;#x26; Testing Your Website&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#optional-configuring-jekyll-for-local-testing&quot;&gt;(Optional) Configuring Jekyll For Local Testing&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;#optional-redirect-your-custom-domain-to-your-jekyll-site&quot;&gt;(Optional) Redirect Your Custom Domain To Your Jekyll Site&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2&gt;Getting Started with GitHub Pages&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/&quot;&gt;GitHub&lt;/a&gt;{:target=&quot;_blank&quot;} is a web-based Git repository hosting service. It offers all of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Distributed_version_control&quot;&gt;distributed version control&lt;/a&gt;{:target=&quot;_blank&quot;} and &lt;a href=&quot;https://en.wikipedia.org/wiki/Version_control&quot;&gt;source code management (SCM)&lt;/a&gt;{:target=&quot;_blank&quot;} functionality of &lt;a href=&quot;https://en.wikipedia.org/wiki/Git&quot;&gt;Git&lt;/a&gt;{:target=&quot;_blank&quot;} as well as adding its own features. After it&apos;s launch in April 2008, the company continued to make improvements to the existing service as well as introducing new features. &lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;{:target=&quot;_blank&quot;} is one of those additions.&lt;/p&gt;
&lt;h3&gt;What is GitHub Pages&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://pages.github.com/&quot;&gt;GitHub Pages&lt;/a&gt;{:target=&quot;_blank&quot;} is designed to host your personal, organization, or project pages directly from a GitHub repository. In order to get started with GitHub Pages, you&apos;ll need to set up a GitHub repository. This is where your content will reside and be served from.&lt;/p&gt;
&lt;h3&gt;Setting up a GitHub Repository&lt;/h3&gt;
&lt;p&gt;Let&apos;s start by creating a new GitHub repository. If you already have a GitHub account, skip to &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;step 4&lt;/span&gt;&lt;/em&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First, create a free &lt;a href=&quot;https://github.com/join&quot;&gt;GitHub account&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/li&gt;
&lt;li&gt;Make sure you pick a proper username (even better if you could find your own name available).&lt;/li&gt;
&lt;li&gt;Download and install &lt;a href=&quot;https://desktop.github.com/&quot;&gt;GitHub desktop&lt;/a&gt;{:target=&quot;_blank&quot;}, especially if you are not comfortable working with &lt;a href=&quot;https://en.wikipedia.org/wiki/Command-line_interface&quot;&gt;CLI&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/li&gt;
&lt;li&gt;Now that you have your GitHub account set up. Go to your GitHub account and create a &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;new repository&lt;/span&gt;&lt;/em&gt; and name it in this format: &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;yourusername.github.io&lt;/span&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 262px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/adc27eedbcb20a7b081e90ffa6572921/8ff13/new_repo_name.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 31.874999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAGCAYAAADDl76dAAAACXBIWXMAAA7EAAAOxAGVKw4bAAABBElEQVR42o2RyU7DMBRF8///gILKgn0HtTsW0BVSJYYWlNKGxE1ayiCkZrDjxIenqMCigLB0dH2v37P0bC/PMvI8xxjNLttRFDnWVmhdUpYlVWUA90/Ai8KI8fiS6Szg+momTFFqzWwaMJncEkVpW+ic++z5c3mN21E3sTSkUr9u1Tbqa98ItVvtURj7+CPaLrH1O56u7lGbDg/hMUHocxf4LOMOCyGMOtzMj5jHPovkhNXmFMOQ6oCR5D1MHeAZG7J+6ZI+DUg2PVTaZ/s6JN0OUEmXMO3KeZ9E/PPbCO3OMQdcSH4mF0Z4zlkZTcuIRlQ+wO219dV31npN0xS/kMvzWD4A5y7FJMKIB70AAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;New Repository&quot;
        title=&quot;&quot;
        src=&quot;/static/adc27eedbcb20a7b081e90ffa6572921/8ff13/new_repo_name.png&quot;
        srcset=&quot;/static/adc27eedbcb20a7b081e90ffa6572921/69538/new_repo_name.png 160w,
/static/adc27eedbcb20a7b081e90ffa6572921/8ff13/new_repo_name.png 262w&quot;
        sizes=&quot;(max-width: 262px) 100vw, 262px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Next, create a copy of your empty repository in your local GitHub repositories folder. To do this:
&lt;ul&gt;
&lt;li&gt;If you are using GitHub desktop, click the &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;Set up in Desktop&lt;/span&gt;&lt;/em&gt; button on your repository page.&lt;/li&gt;
&lt;li&gt;or if you are using CLI, navigate to the folder where you want to store your project, and clone the new repository:&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;git clone https://github.com/username/yourusername.github.io&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ul&gt;
&lt;li&gt;Go to the folder where you have stored your project and create a new file &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;index.html&lt;/span&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Open the file in text editor of your choice and paste the following &lt;code&gt;HTML&lt;/code&gt; in it, this is just to indicate that our site is working expectedly.&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
&amp;lt;html lang=&amp;quot;en&amp;quot;&amp;gt;
    &amp;lt;head&amp;gt;
        &amp;lt;title&amp;gt;My Website&amp;lt;/title&amp;gt;
    &amp;lt;/head&amp;gt;
    &amp;lt;body&amp;gt;
        &amp;lt;h1&amp;gt;My Website Hosted by GitHub Pages&amp;lt;/h1&amp;gt;
    &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ul&gt;
&lt;li&gt;Finally, commit and push changes by:
&lt;ul&gt;
&lt;li&gt;If you are using GitHub Desktop, first write a message in summary field and press &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;Commit To Master&lt;/span&gt;&lt;/em&gt; and then pressing the &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;Sync&lt;/span&gt;&lt;/em&gt; button.&lt;/li&gt;
&lt;li&gt;Or if you are on CLI, use the following commands&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;git add --all
git commit -m &amp;quot;Initial commit&amp;quot;
git push -u origin master&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ul&gt;
&lt;li&gt;Once the changes have been pushed, type in the url &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;yourusername.github.io&lt;/span&gt;&lt;/em&gt; in your browser and you should see your page.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Congratulations! You&apos;ve successfully created your website, now is the time give it a pleasing look and feel.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Setting Up Your Blog/Website&lt;/h2&gt;
&lt;p&gt;Now that your site basic website is up and running, it&apos;s time to make it look like an &lt;em&gt;actual&lt;/em&gt; website with the help of &lt;a href=&quot;https://jekyllrb.com&quot;&gt;Jekyll&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;
&lt;h3&gt;What is Jekyll&lt;/h3&gt;
&lt;p&gt;At this point you might be wondering &lt;em&gt;&lt;code&gt;&apos;so what is Jekyll anyway?&apos;&lt;/code&gt;&lt;/em&gt;. It is an awesome blog-aware website generator that is designed for building minimal, static sites to be hosted on GitHub Pages. I recently migrated my personal site from Wordpress to Jekyll and got to admit that I&apos;m loving it.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;So how does Jekyll differs from Wordpress?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Firstly, if you have used &lt;a href=&quot;www.wordpress.com/&quot;&gt;Wordpress&lt;/a&gt;{:target=&quot;_blank&quot;}, you can tell that it has it&apos;s flaws, plus it&apos;s sort of an overkill if you want to put only simple static content. Also, it has been a hot target for hackers in recent years.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Oh! and did I tell you that it&apos;s waaaay faster than Wordpress?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Wordpress suffers badly as the load grows on the site. However, Jekyll being static-website-centric, completely ends that problem as static files put very little load on the server.&lt;/p&gt;
&lt;p&gt;Another important point for me was &lt;a href=&quot;https://en.wikipedia.org/wiki/Markdown&quot;&gt;Markdown&lt;/a&gt;{:target=&quot;_blank&quot;} support, if you are like me and hate writing &lt;code&gt;HTML&lt;/code&gt; then Jekyll is your friend. Jekyll supports Markdown out of the box, which is a very simple and intuitive text-to-HTML conversion tool. It allows you to write using an easy-to-read, easy-to-write plain text format, then converts it to structurally valid XHTML (or HTML).&lt;/p&gt;
&lt;h3&gt;Give Your Site Personal Touch With Themes&lt;/h3&gt;
&lt;p&gt;Just like Wordpress, Jekyll supports customizable themes. You can find plenty of free themes at &lt;a href=&quot;http://jekyllthemes.org/&quot;&gt;JekyllThemes.org&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;http://themes.jekyllrc.org/&quot;&gt;Themes.JekyllRC.org&lt;/a&gt;{:target=&quot;_blank&quot;}, &lt;a href=&quot;https://jekyllthemes.io/&quot;&gt;JekyllThemes.io&lt;/a&gt;{:target=&quot;_blank&quot;} or at this GitHub &lt;a href=&quot;https://github.com/jekyll/jekyll/wiki/Themes&quot;&gt;repository&lt;/a&gt;{:target=&quot;_blank&quot;}. Our next step will be to apply a theme to our website and then customize it. So go to these sites, play with demo and pick one that you like.&lt;/p&gt;
&lt;p&gt;Once you have found the right theme for your website, download the zip file and extract all of its content to your local repository folder.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;If the archive contains a top level folder and has all the files in it. Then exclude the folder and copy all contents from it to your repository containing folder.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Finally, once again commit and push your changes and open your browser to see your newly furnished site in action.&lt;/p&gt;
&lt;h3&gt;Customizing &amp;#x26; Testing Your Website&lt;/h3&gt;
&lt;p&gt;Jekyll themes come with a &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;_config.yml&lt;/span&gt;&lt;/em&gt; file which contains all the meta data about the website such as title, description, favicon, author info, pages etc. Edit this file with your data e.g.&lt;/p&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
# Website settings

title: &amp;quot;John Doe&amp;quot;
description: &amp;quot;John&amp;#39;s blog powered by Jekyll and GitHub Pages.&amp;quot;
keywords: &amp;quot;John,blog,Jekyll,github,gh-pages&amp;quot;
baseurl: &amp;quot;/&amp;quot;
url: &amp;quot;http://www.JohnDoe.com&amp;quot;

# url: &amp;quot;http://127.0.0.1:4000&amp;quot;

# author

author:
name: &amp;#39;John&amp;#39;
first_name: &amp;#39;John&amp;#39;
last_name: &amp;#39;Doe&amp;#39;
email: &amp;#39;mail@JohnDoe.com&amp;#39;
facebook_username: &amp;#39;JohnDoe&amp;#39;
github_username: &amp;#39;JohnDoe&amp;#39;
head_img: &amp;#39;static/img/landing/JohnDoe.jpg&amp;#39;
desc: &amp;#39;I am a Software Engineer with 5 year experience. I like
coding, swimming and potato.&amp;#39;

# sections

sections:

- id: &amp;#39;about-me&amp;#39;
  i18n: &amp;#39;nav.about_me&amp;#39;
  name: &amp;#39;About&amp;#39;
  tpl: &amp;#39;about.html&amp;#39;
  css: &amp;#39;&amp;#39;

- id: &amp;#39;career&amp;#39;
  i18n: &amp;#39;nav.career&amp;#39;
  name: &amp;#39;Career&amp;#39;
  tpl: &amp;#39;career.html&amp;#39;
  css: &amp;#39;timeline&amp;#39;

- id: &amp;#39;skills&amp;#39;
  i18n: &amp;#39;nav.skills&amp;#39;
  name: &amp;#39;Skills&amp;#39;
  tpl: &amp;#39;skills.html&amp;#39;
  css: &amp;#39;team&amp;#39;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;blockquote&gt;
&lt;p&gt;Contents of file may differ based on the theme you choose&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Once again commit and push your changes and open your browser to see the changes. That&apos;s it! You have your own website up and running totally free of cost. You can continue customizing your theme or learn about writing posts on Jekyll&apos;s &lt;a href=&quot;https://jekyllrb.com/docs/posts/&quot;&gt;official documentation&lt;/a&gt;{:target=&quot;_blank&quot;}.&lt;/p&gt;
&lt;div class=&quot;breaker&quot;&gt;&lt;/div&gt;
&lt;h2&gt;(Optional) Configuring Jekyll For Local Testing&lt;/h2&gt;
&lt;p&gt;At this point, you&apos;re all set to start customizing and publishing your content, but isn&apos;t it tedious to commit and push every time to preview even the slightest of changes?&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Confession:&lt;/strong&gt; I had to push the code over 50 times while customizing my site without knowing there&apos;s an easy way. Make sure you don&apos;t do something as silly as that.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Well good news is that you can set up a local version of your Jekyll GitHub Pages site to test changes to your site locally. It is highly recommended by GitHub to install Jekyll in order to preview your site and troubleshoot failed Jekyll builds.&lt;/p&gt;
&lt;h3&gt;Installing Bundler for Ruby&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Open Git Bash.&lt;/li&gt;
&lt;li&gt;Check whether you have Ruby 2.1.0 or higher installed:&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;ruby --version
ruby 2.X.X&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ul&gt;
&lt;li&gt;If you don&apos;t have Ruby installed, &lt;a href=&quot;https://www.ruby-lang.org/en/downloads/&quot;&gt;install Ruby 2.1.0 or higher&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;After that, install Bundler:&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
gem install bundler&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Installing Jekyll using Bundler&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Check to see if you have a &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;Gemfile&lt;/span&gt;&lt;/em&gt; in your local Jekyll site repository. If you have a Gemfile, skip to &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;step 4&lt;/span&gt;&lt;/em&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you don&apos;t have a Gemfile, open a text editor of your choice, and add these lines to a new file:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;source &amp;#39;https://rubygems.org&amp;#39;
gem &amp;#39;github-pages&amp;#39;, group: :jekyll_plugins&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Name the file &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;Gemfile&lt;/span&gt;&lt;/em&gt; and save it to the root directory of your local Jekyll site repository. Skip to &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;step 5&lt;/span&gt;&lt;/em&gt; to install Jekyll.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you already have a &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;Gemfile&lt;/span&gt;&lt;/em&gt;, open it in a text editor of your choice, and add these lines:&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
source &amp;#39;https://rubygems.org&amp;#39;
gem &amp;#39;github-pages&amp;#39;, group: :jekyll_plugins&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ul&gt;
&lt;li&gt;Finally, install Jekyll and other dependencies from the GitHub Pages gem:&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;bundle install&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;h3&gt;Build your local Jekyll site&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Navigate into the root directory of your local Jekyll site repository.&lt;/li&gt;
&lt;li&gt;Run your Jekyll site locally:&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;
bundle exec jekyll serve&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ul&gt;
&lt;li&gt;Preview your local Jekyll site in your web browser at &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;&lt;a href=&quot;http://localhost:4000&quot;&gt;http://localhost:4000&lt;/a&gt;&lt;/span&gt;&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;div class=&quot;breaker&quot;&gt;&lt;/div&gt;
&lt;h2&gt;(Optional) Redirect Your Custom Domain To Your Jekyll Site&lt;/h2&gt;
&lt;p&gt;If you own a custom domain (say &lt;code&gt;yourusername.com&lt;/code&gt;) then you can redirect this existing domain to your GitHub Pages website so it replaces the yourusername.github.io address with this more personal one.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;First of all create a new file named &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;CNAME&lt;/span&gt;&lt;/em&gt; (all caps, without any spaces or extension)&lt;/li&gt;
&lt;li&gt;Open this file in a text editor of your choice and add your domain name. e.g.&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;yourusername.com&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;
&lt;ul&gt;
&lt;li&gt;Save the file and once again commit and push!&lt;/li&gt;
&lt;li&gt;Next, go to your hosting provider and find the &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;Manage DNS&lt;/span&gt;&lt;/em&gt; section. Add two records there.&lt;/li&gt;
&lt;li&gt;Add a &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;A record&lt;/span&gt;&lt;/em&gt; that points to &lt;code&gt;192.30.252.153&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/1e0471ad076237d3ff2ab4ac21622f9c/dba9a/a_record.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 21.875%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAECAYAAACOXx+WAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAlElEQVR42oVP7QrDIAz0/Z/M332C1R8VtVSLzs/Wm1oGG2w0cCThLpeELHwBpRTTNOExz+CcQ0oJIQSUUljXdfR36NqeSQxhNEoqbJtGSgnvKDkjxohaK+6iHMfQkeA9nHMw+z6IoxG5GZ3n2RZsYIzBWodSyl/0Ga318CF9uBe+GfdrPmGdvV5qHzx/8F9aa2GMwQsawDPr4yrBzQAAAABJRU5ErkJggg==&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;A Record&quot;
        title=&quot;&quot;
        src=&quot;/static/1e0471ad076237d3ff2ab4ac21622f9c/6af66/a_record.png&quot;
        srcset=&quot;/static/1e0471ad076237d3ff2ab4ac21622f9c/69538/a_record.png 160w,
/static/1e0471ad076237d3ff2ab4ac21622f9c/72799/a_record.png 320w,
/static/1e0471ad076237d3ff2ab4ac21622f9c/6af66/a_record.png 640w,
/static/1e0471ad076237d3ff2ab4ac21622f9c/dba9a/a_record.png 652w&quot;
        sizes=&quot;(max-width: 640px) 100vw, 640px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Add a &lt;em&gt;&lt;span class=&quot;evidence&quot;&gt;CNAME record&lt;/span&gt;&lt;/em&gt; that points to your GitHub Pages site:&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;{:refdef: style=&quot;text-align: center;&quot;}
&lt;span
      class=&quot;gatsby-resp-image-wrapper&quot;
      style=&quot;position: relative; display: block; margin-left: auto; margin-right: auto; max-width: 640px; border-radius: 25%;&quot;
    &gt;
      &lt;a
    class=&quot;gatsby-resp-image-link&quot;
    href=&quot;/static/d7a5912ff4f1579dbe4f72cb9445d789/1ac66/cname_record.png&quot;
    style=&quot;display: block&quot;
    target=&quot;_blank&quot;
    rel=&quot;noopener&quot;
  &gt;
    &lt;span
    class=&quot;gatsby-resp-image-background-image&quot;
    style=&quot;padding-bottom: 22.499999999999996%; position: relative; bottom: 0; left: 0; background-image: url(&apos;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAFCAYAAABFA8wzAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAApElEQVR42o2Q3QqDMAyF+/4P197N7WJeOPydQm1qW0E8M0EZbLAZ+OCkSU5CldYaRhsYY1AUBZqmQV3XQlVVwpH/g3vV7XrDPc9xyTJ5iDHCe49pmkSHEESfgXtV2IacI3Cs64ojWLtxBO21M8EHqNE5dM8Oy7JgnmeklHYiyrLcvuGBYRhk6bv2Dc/2fQ/liWCt/TBLso2N2rYV7HbtL0OGiPAClXl+z0nTlRIAAAAASUVORK5CYII=&apos;); background-size: cover; display: block;&quot;
  &gt;&lt;/span&gt;
  &lt;img
        class=&quot;gatsby-resp-image-image&quot;
        alt=&quot;New Repository&quot;
        title=&quot;&quot;
        src=&quot;/static/d7a5912ff4f1579dbe4f72cb9445d789/6af66/cname_record.png&quot;
        srcset=&quot;/static/d7a5912ff4f1579dbe4f72cb9445d789/69538/cname_record.png 160w,
/static/d7a5912ff4f1579dbe4f72cb9445d789/72799/cname_record.png 320w,
/static/d7a5912ff4f1579dbe4f72cb9445d789/6af66/cname_record.png 640w,
/static/d7a5912ff4f1579dbe4f72cb9445d789/1ac66/cname_record.png 651w&quot;
        sizes=&quot;(max-width: 640px) 100vw, 640px&quot;
        style=&quot;width:100%;height:100%;margin:0;vertical-align:middle;position:absolute;top:0;left:0;&quot;
        loading=&quot;lazy&quot;
        decoding=&quot;async&quot;
      /&gt;
  &lt;/a&gt;
    &lt;/span&gt;
{: refdef}&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;And that&apos;s it, wait for changes to propagate and you&apos;ll be able to reach your Jekyll site using your custom domain address.&lt;/li&gt;
&lt;/ul&gt;
&lt;deckgo-highlight-code  theme=&quot;material&quot; line-numbers=&quot;true&quot;  &gt;
          &lt;code slot=&quot;code&quot;&gt;&lt;/code&gt;
        &lt;/deckgo-highlight-code&gt;</content:encoded></item></channel></rss>