<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kshitij Aggarwal</title>
    <description>The latest articles on DEV Community by Kshitij Aggarwal (@funkyidol).</description>
    <link>https://dev.to/funkyidol</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F899%2F4b30062c-9a9f-4e4c-9ccc-8794cb5038d2.jpg</url>
      <title>DEV Community: Kshitij Aggarwal</title>
      <link>https://dev.to/funkyidol</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/funkyidol"/>
    <language>en</language>
    <item>
      <title>2026 New Year Keyboard Upgrade</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Tue, 27 Jan 2026 08:20:59 +0000</pubDate>
      <link>https://dev.to/funkyidol/2026-new-year-keyboard-upgrade-ng3</link>
      <guid>https://dev.to/funkyidol/2026-new-year-keyboard-upgrade-ng3</guid>
      <description>&lt;p&gt;To kick off 2026, I finally did two things I had been postponing: some long overdue switch maintenance and a small but meaningful keycap upgrade.&lt;/p&gt;

&lt;p&gt;The keyboard is a &lt;strong&gt;Pteron36&lt;/strong&gt; designed by &lt;a href="https://keebme.com/pteron36/" rel="noopener noreferrer"&gt;Harshit Goel&lt;/a&gt;. It is a minimalist split build intended to work well with compact layouts like &lt;a href="https://github.com/manna-harbour/miryoku/tree/master" rel="noopener noreferrer"&gt;&lt;strong&gt;Miryoku&lt;/strong&gt;&lt;/a&gt;, where layers + home row modifiers let ~36 keys cover the operations usually spread across an 87–112 key keyboard. &lt;/p&gt;

&lt;p&gt;I also do not type on QWERTY — I use the &lt;strong&gt;Workman&lt;/strong&gt; layout. I will do a separate post on why and how I transitioned. &lt;a href="https://workmanlayout.org" rel="noopener noreferrer"&gt;Workman Layout&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Maintenance: Why and What?
&lt;/h2&gt;

&lt;p&gt;After ~5 years of heavy use and dust + grime built up, the switches started feeling &lt;strong&gt;scratchy&lt;/strong&gt;. The ideal fix is usually to open each switch and lube the stem + springs—but my switches are soldered to the PCB, and I did not have the bandwidth or the motivation to desolder and fully disassemble everything.&lt;/p&gt;

&lt;p&gt;So I went with &lt;strong&gt;lazy lubing&lt;/strong&gt;: pressed the stem down and apply lube to the inside walls of the housing and the exposed sides of the stem. It does not reach the springs, but it can still reduce friction and smooth out the keypress.&lt;/p&gt;

&lt;p&gt;For lube, I used &lt;strong&gt;Krytox GPL 205g0&lt;/strong&gt;, a common choice in the keyboard hobby.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4r1ssaa576xbnpidh79n.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F4r1ssaa576xbnpidh79n.jpeg" alt="Photo of dismantled keys" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Upgrade: Keycaps That Fake a Key Well
&lt;/h2&gt;

&lt;p&gt;I had originally planned a bigger upgrade: moving to a &lt;a href="https://github.com/qmk/qmk_firmware/blob/master/keyboards/handwired/dactyl_manuform/readme.md" rel="noopener noreferrer"&gt;&lt;strong&gt;Dactyl Manuform style&lt;/strong&gt;&lt;/a&gt; board with a concave "key well," since that curvature can make finger travel feel more natural.&lt;/p&gt;

&lt;p&gt;But after talking to a few folks in the ergonomic keyboard community, I pivoted to a lighter change: trying &lt;a href="https://github.com/braindefender/KLP-Lame-Keycaps" rel="noopener noreferrer"&gt;&lt;strong&gt;KLP Lamé&lt;/strong&gt;&lt;/a&gt; keycaps. These are sculpted, curved keycaps designed to reduce vertical finger travel and create a &lt;em&gt;pseudo key-well&lt;/em&gt; effect on flatter boards.&lt;/p&gt;

&lt;p&gt;I found a 3D printing service that could print them in resin. The shape makes the rows feel "guided", especially when reaching above/below the home row.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmw1vqrtbjjr15qtdqwb.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzmw1vqrtbjjr15qtdqwb.jpeg" alt="Closeup of sculpted keycaps" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Result
&lt;/h2&gt;

&lt;p&gt;After lubing, the board feels noticeably smoother and the scratchiness is largely gone. The keycaps are still an adjustment, but it is a good one—my fingers feel like they "land" sooner than they used to, especially on vertical reaches.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81cew34aniv3tz5zsq3w.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F81cew34aniv3tz5zsq3w.jpeg" alt="Completed keyboard" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;




</description>
      <category>keyboards</category>
      <category>miryoku</category>
      <category>engonomic</category>
    </item>
    <item>
      <title>Beyond the Visuals: Why Audio UX is Critical in Android XR</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Sat, 27 Dec 2025 13:27:22 +0000</pubDate>
      <link>https://dev.to/funkyidol/beyond-the-visuals-why-audio-ux-is-critical-in-android-xr-2pj2</link>
      <guid>https://dev.to/funkyidol/beyond-the-visuals-why-audio-ux-is-critical-in-android-xr-2pj2</guid>
      <description>&lt;h3&gt;
  
  
  Lessons learned from my first Android XR sample app.
&lt;/h3&gt;

&lt;p&gt;Audio UX is often treated as an afterthought in application development, but in Android XR and smart glass experiences, it has to be a priority.&lt;/p&gt;

&lt;p&gt;I recently built my first Android XR sample app. Drawing from my years of experience building software for smart glasses an Envision AI, one truth remains constant: &lt;strong&gt;robust Audio feedback is the difference between a novelty demo and a successful daily-driver product.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Why: It’s Not Just About the Screen
&lt;/h2&gt;

&lt;p&gt;Why should developers prioritize audio in a visual medium?&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;The "Real World" Distraction&lt;/p&gt;

&lt;p&gt;Unlike VR, where the user is fully enclosed, XR and Augmented Reality allow the user to see the real world. There is a high probability the user is focused on their environment, not your "Heads Up Display." Audio cues grab attention without forcing the user to refocus their eyes.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Display Constraints&lt;/p&gt;

&lt;p&gt;In some smart glass form factors, the display may be off or missing altogether, or the user may be in bright sunlight where optics are washed out. In these scenarios, voice becomes the primary interface.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Accessibility is Mandatory&lt;/p&gt;

&lt;p&gt;For Blind and Low Vision users, a visual-only AR interface is unusable. Voice output is not just a feature; it is the only bridge to understanding what is happening in the application.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  What constitutes a Good Audio UX
&lt;/h2&gt;

&lt;p&gt;To build a cohesive Audio UX, your application needs to cover these bases:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Welcome Greeting and Exit salutation:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Always let user know when the app starts and stops to ensure they are aware if the app is working.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Touch bar feedback:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;All Touch area interactions should have an immediate audio feedback so that users know if there action was registered&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Interaction updates:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Give verbal updates of the current status of the user requested action. For eg. Loading, Success or Error state&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;User requested status updates:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;User may not always be paying attention to what's happening in the app and forget what the current state of the app. A single tap by the user should be able to tell the user about the current status of the app or the result of their last interaction.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Verbal vs. Non-Verbal
&lt;/h2&gt;

&lt;p&gt;While chimes and beeps (Earcons) are great for clicks, they have a steep learning curve for complex status updates. Don't force users to memorize what "Beep-Boop-Boop" means.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rule of Thumb:&lt;/strong&gt; Use sounds for actions (clicks, swipes), and use &lt;strong&gt;Text-to-Speech (TTS)&lt;/strong&gt; for information. It is always clearer to speak the output in the user's language than to rely on abstract sounds.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;With the advent of Android XR, we are going to see a flood of applications targeting head-worn devices. While current marketing hype focuses heavily on the visual fidelity of VR and AR, the most successful applications will be the ones that master the invisible interface: &lt;strong&gt;Audio.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>androiddev</category>
      <category>androidxr</category>
      <category>smartglasses</category>
      <category>a11y</category>
    </item>
    <item>
      <title>Moving Away from Gitflow and Feature Branches</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Thu, 17 Jul 2025 04:45:19 +0000</pubDate>
      <link>https://dev.to/funkyidol/moving-away-from-gitflow-and-feature-branches-3cg0</link>
      <guid>https://dev.to/funkyidol/moving-away-from-gitflow-and-feature-branches-3cg0</guid>
      <description>&lt;h2&gt;
  
  
  &lt;strong&gt;Intro&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;As the title suggests, this post is a case study on how Gitflow and traditional feature branching have been hindering our current development process. I'll share the attempts I'm making to resolve some of the key issues we're facing as an Android development team.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Current Setup &amp;amp; Issues&lt;/strong&gt;
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;The Setup&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;We are a small, fully-remote team of developers who have been using Gitflow as our Git branching model, coupled with a standard Merge Request (MR) and Code Review process.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code Repository &amp;amp; Planning:&lt;/strong&gt; We use GitLab as our code repository, leveraging its issues feature for planning and tracking our development work.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Workflow:&lt;/strong&gt; All planned work first moves into our Kanban board, gets assigned, and is then picked up based on priority. Tickets are broadly defined, with one issue per feature or significant piece of work, regardless of its size.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Development Process:&lt;/strong&gt; Once a developer picks up a task:

&lt;ul&gt;
&lt;li&gt;They create a Gitflow-style feature branch in the format feature/&amp;lt;issue-id&amp;gt;-&amp;lt;issue-title&amp;gt; from our dev branch.
&lt;/li&gt;
&lt;li&gt;We maintain Gitflow-style branches for our active development, beta, and production channels, using release tags to mark each public beta and production release.
&lt;/li&gt;
&lt;li&gt;Developers work in isolation within their feature branches until the assigned work is completely done, tested, and verified. Although we might switch between branches for urgent bugs or enhancements, the core feature work remains siloed.
&lt;/li&gt;
&lt;li&gt;Consequently, a feature—regardless of whether it takes a day, a week, or two weeks—is never merged back into dev until its Merge Request is created, and the code is thoroughly tested, reviewed, and approved by other team members.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Issues Faced&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Delayed Shipping:&lt;/strong&gt; Features face multiple bottlenecks, remaining unshipped until development, testing, review, merging, &lt;em&gt;and&lt;/em&gt; re-testing are all complete.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Review Process Overheads:&lt;/strong&gt; The process is further complicated by potential issues raised during reviews and inevitable merge conflicts.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code Review Quality:&lt;/strong&gt; Reviewing becomes challenging and less effective when Merge Requests contain hundreds of new or modified lines.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Availability Challenges:&lt;/strong&gt; Other team members are not always consistently available to review such large code changes in one go.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge Conflict Headaches:&lt;/strong&gt; We often face the painstaking task of fixing merge conflicts, trying not to mess up the original developer's work.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ineffective dev Merges:&lt;/strong&gt; While we try to regularly merge dev into active feature branches, this doesn't guarantee a conflict-free merge when integrating other people's work.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release Delays:&lt;/strong&gt; Ultimately, our feature releases end up getting delayed because they're waiting on multiple rounds of merges and approvals.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Search for a Solution&lt;/strong&gt;
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problem Identification:&lt;/strong&gt; After being repeatedly impacted by our current process, I wanted to find possible ways to fix this problem.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Belief in Solved Problem:&lt;/strong&gt; I was sure this must be a solved problem, as we couldn't be the only small team facing this issue.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Inspiration from Dave Farley:&lt;/strong&gt; Luckily, around this time, I came across a video by Dave Farley discussing a similar topic and how feature branching often goes against the philosophy of Continuous Integration (CI).
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep Dive:&lt;/strong&gt; This led me down a rabbit hole in search of alternative solutions to make our development process faster, without duplicated work or additional delays.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Learnings&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Here are some key learnings from my deep dive:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code reviews don't primarily check for stability.&lt;/strong&gt; Their main purpose is to ensure the code adheres to team-defined standards.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Delaying releases for code reviews doesn't ensure stable output.&lt;/strong&gt; Only thorough testing guarantees stability.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge Requests were primarily introduced for open-source projects&lt;/strong&gt; where contributors were not given direct access to push code to stable branches.
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Merge Requests and code reviews are often considered "process gates."&lt;/strong&gt; These can slow down the overall development process without adding significant benefits to the quality of the deliverable or its underlying implementation.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Shortlisted Ideas&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;After reviewing a bunch of reference material and having multiple discussions with other people in this field, I found the following three solutions that, when integrated, might work for our team:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Vertical Slicing&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;No more single, monolithic tickets per feature.
&lt;/li&gt;
&lt;li&gt;Each feature is meticulously broken down into smaller tasks, ensuring only small chunks of work are completed and committed.
&lt;/li&gt;
&lt;li&gt;Each major feature or change is considered an epic, and then sub-tasks are created to slice out smaller, logical chunks.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Trunk-Based Development (TBD)&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The team develops off of the same main branch without creating dedicated feature branches.
&lt;/li&gt;
&lt;li&gt;Developers push their changes directly to this main branch, and these changes are immediately available to everyone else when they pull.
&lt;/li&gt;
&lt;li&gt;This approach ensures the codebase is always merged and conflict-free at all times.
&lt;/li&gt;
&lt;li&gt;Combined with vertical slicing, commits become small, easy to merge, and straightforward to review.
&lt;/li&gt;
&lt;li&gt;It removes much of the ceremony around code merging and code reviews, as these now happen continuously, often multiple times a day.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Feature Flags&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;To hide unfinished and unstable work, feature flags are used to safely ship releases containing completed work from the main development branch.
&lt;/li&gt;
&lt;li&gt;Developers can toggle features on/off at any time to test work developed by others and themselves.
&lt;/li&gt;
&lt;li&gt;To ship a feature to users, once it's fully tested, its respective feature flag can either be entirely removed or migrated to a remote configuration flag to control public availability.
&lt;/li&gt;
&lt;li&gt;This practice is also known as "Dark Releases."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some other solutions I researched but decided not to pursue for now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Pair Programming:&lt;/strong&gt; Being a very small team, pair programming was never a viable option for us due to limited resource allocation. While it's a great concept popularized by Extreme Programming, it doesn't fit our current constraints.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Updated Process (as of July 2025)&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Based on the shortlisted solutions above, I plan to introduce the following changes to our current development process:&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;GitLab Vertical Slicing&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;We will continue to create issues for features and tasks, but now, within each GitLab issue, we will create sub-tasks to define smaller, more manageable slices of work.
&lt;/li&gt;
&lt;li&gt;In GitLab, each sub-task gets its own unique ID. We will use this ID in our commit messages to connect the sub-task with the commit.
&lt;/li&gt;
&lt;li&gt;If a sub-task proves too large or lacks detail during development, we will create additional sub-tasks within the same ticket. The objective is always to ensure each sub-task represents a limited amount of work, resulting in minimal commits and changes.
&lt;/li&gt;
&lt;li&gt;One slicing strategy will be based on architectural layers. Every feature can be broken into a minimum of four sub-tasks, one for each:

&lt;ul&gt;
&lt;li&gt;Data Layer
&lt;/li&gt;
&lt;li&gt;Domain Layer
&lt;/li&gt;
&lt;li&gt;UI Layer
&lt;/li&gt;
&lt;li&gt;Integration of all three layers&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;dev-Based Development (Trunk-Based)&lt;/strong&gt;
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;All development will happen directly on the dev branch. We will no longer use Gitflow-based feature branching.
&lt;/li&gt;
&lt;li&gt;Feature branches will only be used for rare, truly experimental work.
&lt;/li&gt;
&lt;li&gt;Developers are expected to self-test and self-review all code thoroughly &lt;em&gt;before&lt;/em&gt; pushing to the origin.
&lt;/li&gt;
&lt;li&gt;Code reviews will happen continuously: every time new changes are pulled from the origin. There will be no more explicit code review flow; everyone is expected to review the changes being pulled.
&lt;/li&gt;
&lt;li&gt;Any code review comments will be communicated via the GitLab commit screen.
&lt;/li&gt;
&lt;li&gt;Commits should contain the sub-task ID in the commit message. If relevant, create a new sub-task if one is missing.
&lt;/li&gt;
&lt;li&gt;Commits should be pushed to the origin at least once a day.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Dark Releases with Feature Flags&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;Implementing this process requires some project-level setup. This is critical to ensure unfinished features and changes are never accidentally shipped to the public.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Feature Flags will live in their own independent data store (e.g., a dedicated feature flag service or a specific database table).
&lt;/li&gt;
&lt;li&gt;Feature Flags can be toggled on/off from a developer-only menu within the app.
&lt;/li&gt;
&lt;li&gt;Every new feature development will begin with the creation of its unique feature flag.
&lt;/li&gt;
&lt;li&gt;All Feature Flags will automatically be disabled for release builds to prevent accidental feature releases to the public.
&lt;/li&gt;
&lt;li&gt;Once a feature is completed and tested internally, its feature flag will either be fully removed or migrated to a remote configuration flag for controlled public availability.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  &lt;strong&gt;Conclusion&lt;/strong&gt;
&lt;/h2&gt;

&lt;p&gt;Moving away from our old Gitflow process is a big step for our Android team. By using Vertical Slicing, Trunk-Based Development, and Feature Flags, we aim to make our work smoother, remove roadblocks, and get new features out faster. &lt;br&gt;
We believe these changes will help us work together better, integrate code more often, and lead to better quality, fewer problems, and quicker releases. This is all about making our development process work best for our small, remote team, so we can deliver great features more easily and with more confidence. We also understand that this process is new, and we're open to making more changes as our team gets used to it and grows.&lt;/p&gt;




&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://danielkummer.github.io/git-flow-cheatsheet/" rel="noopener noreferrer"&gt;Gitflow Cheatsheet&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=v4Ijkq6Myfc" rel="noopener noreferrer"&gt;Continuous Integration vs Feature Branch Workflow - YouTube&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=ASOSEiJCyEM" rel="noopener noreferrer"&gt;Why Pull Requests Are A BAD IDEA - YouTube&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.youtube.com/watch?v=WmVe1QrWxYU" rel="noopener noreferrer"&gt;I’ve Found Something BETTER Than Pull Requests... - YouTube&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Android Logging Performance Improvements in Production</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Thu, 20 Jun 2024 06:59:38 +0000</pubDate>
      <link>https://dev.to/funkyidol/timber-logging-performance-improvements-in-production-56k7</link>
      <guid>https://dev.to/funkyidol/timber-logging-performance-improvements-in-production-56k7</guid>
      <description>&lt;p&gt;When logging in production using timber, you can easily exclude certain log levels to prevent sensitive debug data from transmitting online.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;inner&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CrashReportingTree&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Timber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="n"&gt;logMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;ERROR&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;INFO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nc"&gt;FirebaseCrashlytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nc"&gt;FirebaseCrashlytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;recordException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this doesnt do is ignore the said logs. If timber comes across a debug log in the above case, it will still be processed completely until the time comes to actually log it, for e.g. operations like formatting and splitting. This can create a performance overhead in production if the logging is being done generously and with potentially large data like network request results.&lt;/p&gt;

&lt;p&gt;There is a way where you can tell Timber if it should ignore certain log types completely and not process the log message at all.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CrashReportingTree&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Timber&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Tree&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="n"&gt;logMessage&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Throwable&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="nf"&gt;isLoggable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="c1"&gt;// Skip logging if not loggable  &lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
            &lt;span class="nc"&gt;FirebaseCrashlytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;logMessage&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;throwable&lt;/span&gt; &lt;span class="p"&gt;!=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
                &lt;span class="nc"&gt;FirebaseCrashlytics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;recordException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;throwable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  
            &lt;span class="p"&gt;}&lt;/span&gt;  
        &lt;span class="p"&gt;}&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;  

    &lt;span class="c1"&gt;// Overridden to stop processing of all logs less then info level within Timber  &lt;/span&gt;
    &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;isLoggable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;?,&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;priority&lt;/span&gt; &lt;span class="p"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;INFO&lt;/span&gt;  
    &lt;span class="p"&gt;}&lt;/span&gt;  
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Overriding &lt;code&gt;isLoggable&lt;/code&gt; tells the Timber api to match and process only allowed log types internally before it does any processing&lt;br&gt;
&lt;a href="https://github.com/JakeWharton/timber/blob/e6db346a2934a55bb940d0a5d293a931f54de066/timber/src/main/java/timber/log/Timber.kt#L149"&gt;timber/timber/src/main/java/timber/log/Timber.kt&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hope this brings some finer performance boost to your apps&lt;/p&gt;

</description>
      <category>androiddev</category>
      <category>performance</category>
      <category>crashlytics</category>
      <category>android</category>
    </item>
    <item>
      <title>Exploring CameraX API Beta (Part -2): Image Analysis</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Sat, 13 Jun 2020 14:02:05 +0000</pubDate>
      <link>https://dev.to/funkyidol/exploring-camerax-api-beta-part-2-image-analysis-1hha</link>
      <guid>https://dev.to/funkyidol/exploring-camerax-api-beta-part-2-image-analysis-1hha</guid>
      <description>&lt;p&gt;&lt;em&gt;Update since the previous post: &lt;div class="ltag__link"&gt;
  &lt;a href="/funkyidol" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F899%2F4b30062c-9a9f-4e4c-9ccc-8794cb5038d2.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F899%2F4b30062c-9a9f-4e4c-9ccc-8794cb5038d2.jpg" alt="funkyidol"&gt;&lt;/a&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/funkyidol/exploring-camerax-beta-release-part-1-19hh" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Exploring CameraX Beta (Part - 1): Basic Setup&lt;/h2&gt;
      &lt;h3&gt;Kshitij Aggarwal ・ May 18 '20&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#android&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#kotlin&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#camerax&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;
&lt;/em&gt;&lt;br&gt;
Google has release a new beta update (1.0.0-beta4) to the CameraX api and one breaking change is that when setting the &lt;code&gt;createSurfaceProvider()&lt;/code&gt; on the &lt;code&gt;viewFinder&lt;/code&gt; we dont need to add the &lt;code&gt;cameraInfo&lt;/code&gt; parameter. A very small and simple change that makes things a little more simpler.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;setSurfaceProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSurfaceProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;cameraInfo&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="c1"&gt;// changes to &lt;/span&gt;
&lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;setSurfaceProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSurfaceProvider&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;p&gt;In our previous post we looked at how to setup CameraX api and create a simple camera preview screen within a fragment. In this post we will talk about how to setup an 'Analysis' use-case which will use the MLKit on-device text detection to read text. There are many samples available online which show this exact same thing but what I wanted to share is how to start and stop the analysis on-demand. Every sample that I have seen till now starts the analysis on camera start and does not show how you can stop it when required.&lt;/p&gt;
&lt;h2&gt;
  
  
  Text Detection Implementation Steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Include the following dependency in the module level &lt;code&gt;build.gradle&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;  &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'com.google.android.gms:play-services-mlkit-text-recognition:16.0.0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Add the following meta data to the &lt;code&gt;AndroidManifest.xml&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;  &lt;span class="nt"&gt;&amp;lt;application&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    ...
    &lt;span class="nt"&gt;&amp;lt;meta-data&lt;/span&gt;
        &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"com.google.mlkit.vision.DEPENDENCIES"&lt;/span&gt;
        &lt;span class="na"&gt;android:value=&lt;/span&gt;&lt;span class="s"&gt;"ocr"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="c"&gt;&amp;lt;!-- To use multiple models: android:value="ocr,model2,model3" --&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/application&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Create a new &lt;code&gt;TextAnalyzer&lt;/code&gt; class and extend &lt;code&gt;ImageAnalysis.Analyzer&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;  &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;TextAnalyzer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;result&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ImageAnalysis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Analyzer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;We will also take a callback function parameter to send result back to the calling class&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Override the &lt;code&gt;analyze&lt;/code&gt; method
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;  &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;analyze&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;imageProxy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ImageProxy&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Get the &lt;code&gt;TextRecognizer&lt;/code&gt; instance&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;recognizer&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;TextRecognition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Get the image from the &lt;code&gt;imageProxy&lt;/code&gt; param&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;mediaImage&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;imageProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If the &lt;code&gt;mediaImage&lt;/code&gt; is not null then execute the following code. Here we create the &lt;code&gt;InputImage&lt;/code&gt; from the &lt;code&gt;mediaImage&lt;/code&gt; and the &lt;code&gt;rotationDegrees&lt;/code&gt; value from the &lt;code&gt;imageProxy&lt;/code&gt;. We then send this &lt;code&gt;inputImage&lt;/code&gt; to the &lt;code&gt;recognizer&lt;/code&gt; for processing. In case of both success and failure we ensure that the &lt;code&gt;imageProxy.close()&lt;/code&gt; is always called so CameraX api knows that the current frame processing is complete and it can move onto capturing the next frame.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;    &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;InputImage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromMediaImage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;mediaImage&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;imageProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;imageInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rotationDegrees&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;recognizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;process&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;image&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOnSuccessListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Text ${text.text}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;imageProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addOnFailureListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;e&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Mlkit processing Failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;imageProxy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;In our camera fragment (&lt;code&gt;MainFragment&lt;/code&gt;) we now create the analysis use case and attach to the  camera object. Please note that for now we are just attaching the use case and not the analyzer itself which we just created.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;  &lt;span class="n"&gt;imageAnalysis&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ImageAnalysis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                              
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTargetResolution&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Size&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1280&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;720&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;                            
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setBackpressureStrategy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ImageAnalysis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;STRATEGY_KEEP_ONLY_LATEST&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                                                         
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Camera binding now changes to&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;  &lt;span class="n"&gt;camera&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt;
      &lt;span class="n"&gt;cameraProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindToLifecycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="k"&gt;this&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;LifecycleOwner&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;cameraSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="n"&gt;imageAnalysis&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;We now add a button to the UI to enable and disable the text analysis on demand
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;  &lt;span class="n"&gt;btn_analyze&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setOnClickListener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;featureOn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;featureOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&lt;/span&gt;
          &lt;span class="n"&gt;imageAnalysis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAnalyzer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cameraExecutor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;TextAnalyzer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
              &lt;span class="n"&gt;tv_result&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt;
          &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="n"&gt;featureOn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
          &lt;span class="n"&gt;imageAnalysis&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;clearAnalyzer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;So now, when we have to turn on the text analysis we create a new instance of the &lt;code&gt;TextAnalyzer&lt;/code&gt; class and set it to the &lt;code&gt;imageAnalysis&lt;/code&gt; use-case. And on it callback result we show the text on the screen to show the detected text.&lt;br&gt;
  When we turn off the the text analysis, we just clear the &lt;code&gt;imageAnalysis&lt;/code&gt; use-case from all the attached analyzers.&lt;/p&gt;

&lt;p&gt;And that's it. Just by attaching and clearing the use-cases we can create on-demand analysis features using the CameraX api. You can find the complete source code for this post on the commit &lt;a href="https://github.com/funkyidol/CameraXSample/commit/4397d1249ea49b6f2e13c10cc5be7c8adedb38f8"&gt;here&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/funkyidol"&gt;
        funkyidol
      &lt;/a&gt; / &lt;a href="https://github.com/funkyidol/CameraXSample"&gt;
        CameraXSample
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Quick sample and accompanying source code on how to integrate the new CameraX API
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;



</description>
      <category>android</category>
      <category>camerax</category>
      <category>kotlin</category>
    </item>
    <item>
      <title>Exploring CameraX Beta (Part - 1): Basic Setup</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Mon, 18 May 2020 05:34:29 +0000</pubDate>
      <link>https://dev.to/funkyidol/exploring-camerax-beta-release-part-1-19hh</link>
      <guid>https://dev.to/funkyidol/exploring-camerax-beta-release-part-1-19hh</guid>
      <description>&lt;p&gt;CameraX is probably one of the most requested API's that most developers dealing with the Android camera API setup are looking forward to. What the team developing the CameraX API is doing is no easy feat and this also shows in the development path of the API with pretty massive changes happening throughout the Alpha development stage. (&lt;a href="https://developer.android.com/jetpack/androidx/releases/camera"&gt;Release notes &amp;amp; history&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;This blog post is first of the series on CameraX where I try and explore the API (1.0.0-beta03 at the time of writing this article) in a more in-depth manner at an implementation level with a real-use setup (read 'fragments') since most of the CameraX explorations I have found have been prior to the &lt;a href="https://developer.android.com/jetpack/androidx/releases/camera#camera-core-1.0.0-alpha07"&gt;1.0.0-alpha7&lt;/a&gt; release, which by now have been changed significantly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementation Steps
&lt;/h2&gt;

&lt;p&gt;The following are the implementation steps I followed to get a simple camera preview using CameraX&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Include the following gradle dependencies in your project level &lt;code&gt;build.gradle&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight groovy"&gt;&lt;code&gt;   &lt;span class="n"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// CameraX core library&lt;/span&gt;
       &lt;span class="kt"&gt;def&lt;/span&gt; &lt;span class="n"&gt;camerax_version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'1.0.0-beta03'&lt;/span&gt;
       &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"androidx.camera:camera-core:$camerax_version"&lt;/span&gt;

       &lt;span class="c1"&gt;// CameraX Camera2 extensions&lt;/span&gt;
       &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"androidx.camera:camera-camera2:$camerax_version"&lt;/span&gt;

       &lt;span class="c1"&gt;// CameraX Lifecycle library&lt;/span&gt;
       &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s2"&gt;"androidx.camera:camera-lifecycle:$camerax_version"&lt;/span&gt;

       &lt;span class="c1"&gt;// CameraX View class&lt;/span&gt;
       &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.camera:camera-view:1.0.0-alpha10'&lt;/span&gt;
       &lt;span class="n"&gt;implementation&lt;/span&gt; &lt;span class="s1"&gt;'androidx.fragment:fragment:1.2.4'&lt;/span&gt;
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add the &lt;code&gt;PreviewView&lt;/code&gt; component in the fragment layout XML
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;androidx.camera.view.PreviewView&lt;/span&gt;
       &lt;span class="na"&gt;android:id=&lt;/span&gt;&lt;span class="s"&gt;"@+id/view_finder"&lt;/span&gt;
       &lt;span class="na"&gt;android:layout_width=&lt;/span&gt;&lt;span class="s"&gt;"match_parent"&lt;/span&gt;
       &lt;span class="na"&gt;android:layout_height=&lt;/span&gt;&lt;span class="s"&gt;"match_parent"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add the Camera permissions to the &lt;code&gt;AndroidManifest.xml&lt;/code&gt; and implement the permissions provider in your fragment
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;   &lt;span class="nt"&gt;&amp;lt;uses-permission&lt;/span&gt; &lt;span class="na"&gt;android:name=&lt;/span&gt;&lt;span class="s"&gt;"android.permission.CAMERA"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add the following method to add the camera preview use-case to the fragment
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;bindCameraUseCases&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

           &lt;span class="c1"&gt;// Get screen metrics used to setup camera for full screen resolution&lt;/span&gt;
           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;metrics&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DisplayMetrics&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;also&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;viewFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRealMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
           &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Screen metrics: ${metrics.widthPixels} x ${metrics.heightPixels}"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;screenAspectRatio&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;aspectRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;widthPixels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;heightPixels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
           &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;d&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Preview aspect ratio: $screenAspectRatio"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;rotation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rotation&lt;/span&gt;

           &lt;span class="c1"&gt;// Bind the CameraProvider to the LifeCycleOwner&lt;/span&gt;
           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;cameraSelector&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CameraSelector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;requireLensFacing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lensFacing&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
           &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;cameraProviderFuture&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProcessCameraProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;requireContext&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
           &lt;span class="n"&gt;cameraProviderFuture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Runnable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

               &lt;span class="c1"&gt;// CameraProvider&lt;/span&gt;
               &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;cameraProvider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ProcessCameraProvider&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cameraProviderFuture&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

               &lt;span class="c1"&gt;// Preview&lt;/span&gt;
               &lt;span class="n"&gt;preview&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Preview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                   &lt;span class="c1"&gt;// We request aspect ratio but no resolution&lt;/span&gt;
                   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTargetAspectRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;screenAspectRatio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                   &lt;span class="c1"&gt;// Set initial target rotation&lt;/span&gt;
                   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTargetRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                   &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

               &lt;span class="c1"&gt;// Must unbind the use-cases before rebinding them&lt;/span&gt;
               &lt;span class="n"&gt;cameraProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unbindAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

               &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="c1"&gt;// A variable number of use-cases can be passed here -&lt;/span&gt;
                   &lt;span class="c1"&gt;// camera provides access to CameraControl &amp;amp; CameraInfo&lt;/span&gt;
                   &lt;span class="n"&gt;camera&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cameraProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindToLifecycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cameraSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

                   &lt;span class="c1"&gt;// Attach the viewfinder's surface provider to preview use case&lt;/span&gt;
                   &lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;setSurfaceProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSurfaceProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;cameraInfo&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
               &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                   &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;e&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Use case binding failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
               &lt;span class="p"&gt;}&lt;/span&gt;

           &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="nc"&gt;ContextCompat&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getMainExecutor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;requireContext&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Add the following helper method to find the correct aspect ratio of the device and set it to CameraX
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;aspectRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nc"&gt;Int&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;previewRatio&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;max&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toDouble&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;/&lt;/span&gt; &lt;span class="nf"&gt;min&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;height&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previewRatio&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="nc"&gt;RATIO_4_3_VALUE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="nf"&gt;abs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;previewRatio&lt;/span&gt; &lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="nc"&gt;RATIO_16_9_VALUE&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;AspectRatio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RATIO_4_3&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
       &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;AspectRatio&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;RATIO_16_9&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Start the camera
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;   &lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;startCamera&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="n"&gt;viewFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
           &lt;span class="nf"&gt;bindCameraUseCases&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
       &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's all folks. You just need to handle the camera permissions in the usual manner and the above code will give you the basic camera preview setup. Now lets dig a little deeper and see what are we actually doing here.&lt;/p&gt;

&lt;h2&gt;
  
  
  Explanation
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Find the device aspect ratio and rotation values&lt;/strong&gt;
We will use the &lt;code&gt;DisplayMetrics&lt;/code&gt; API to find these values and use them later in both setting up CameraX as well as in future scenarios send this to MLKit.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;metrics&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;DisplayMetrics&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;also&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;viewFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRealMetrics&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;screenAspectRatio&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;aspectRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;widthPixels&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metrics&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;heightPixels&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;rotation&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;viewFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;display&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;rotation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Select which camera to use and create a &lt;code&gt;ListenableFuture&lt;/code&gt; of the &lt;code&gt;ProcessCameraProvider&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;cameraSelector&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CameraSelector&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;requireLensFacing&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lensFacing&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;cameraProviderFuture&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;ProcessCameraProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getInstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;requireContext&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;ListenableFuture&lt;/code&gt; in short allows you to register callbacks to be executed once the asynchronous computation is complete, or if the computation is already complete, immediately. You can find more details in the &lt;a href="https://github.com/google/guava/wiki/ListenableFutureExplained"&gt;official documentation&lt;/a&gt;&lt;br&gt;
   Callback for this &lt;code&gt;cameraProviderFuture&lt;/code&gt; contains the logic for when the &lt;code&gt;ProcessCameraProvider&lt;/code&gt; is available to prepare it to be initialized by binding it to the lifecycle and required use cases&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prepare the camera preview use case&lt;/strong&gt;
Here we prepare the camera preview use case using the aspect ratio and rotation values calculated earlier.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;   &lt;span class="n"&gt;preview&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Preview&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Builder&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
       &lt;span class="c1"&gt;// We request aspect ratio but no resolution&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTargetAspectRatio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;screenAspectRatio&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="c1"&gt;// Set initial target rotation&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setTargetRotation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;rotation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
       &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Use-cases provide a very clear abstraction between the three main things we generally use camera for - preview the camera feed, capture from the camera feed and analyse the captured frames. CameraX provide these three use-cases as separate classes which we can extend as per our requirement. You can find more details about the use-cases in the &lt;a href="https://developer.android.com/training/camerax/architecture#combine-use-cases"&gt;official documentation&lt;/a&gt; and we will explore them further in future articles in this series.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bind the &lt;code&gt;ProcessCameraProvider&lt;/code&gt; to the lifecycle along with the use-cases&lt;/strong&gt;
First we have to unbind all the existing use-cases from the camera provider. If not it will throw runtime exception
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;   &lt;span class="n"&gt;cameraProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;unbindAll&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Binding the &lt;code&gt;ProcessCameraProvider&lt;/code&gt; to the lifecycle is a must as this allows the CameraX API to automatically manage the camera resources bases on the provided lifecycle, reducing a lot of boilerplate code needed earlier with &lt;code&gt;Camera2&lt;/code&gt; API. We also attach our use-case here, &lt;code&gt;Preview&lt;/code&gt; use-case in our sample, for the CameraX API to show the camera preview.  We now take this post-binding camera instance and set it to the &lt;code&gt;PreviewView&lt;/code&gt; object from the layout xml. This is where the preview will be rendered on the UI&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;   &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="c1"&gt;// A variable number of use-cases can be passed here -&lt;/span&gt;
       &lt;span class="c1"&gt;// camera provides access to CameraControl &amp;amp; CameraInfo&lt;/span&gt;
       &lt;span class="n"&gt;camera&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;cameraProvider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bindToLifecycle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cameraSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

       &lt;span class="c1"&gt;// Attach the viewfinder's surface provider to preview use case&lt;/span&gt;
       &lt;span class="n"&gt;preview&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="nf"&gt;setSurfaceProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;viewFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createSurfaceProvider&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;camera&lt;/span&gt;&lt;span class="o"&gt;?.&lt;/span&gt;&lt;span class="n"&gt;cameraInfo&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Exception&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;e&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;TAG&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Use case binding failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;exc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start the camera&lt;/strong&gt;
Calling the 'bind' code in  &lt;code&gt;post&lt;/code&gt; on the &lt;code&gt;viewFinder&lt;/code&gt; ensures that the code is called on the UI thread, which is mandatory for initiating the CameraX API. Not doing this will give a run-time exception.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;   &lt;span class="n"&gt;viewFinder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;post&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
       &lt;span class="nf"&gt;bindCameraUseCases&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
   &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And now we have a fully functional camera preview app using CameraX. You can find the complete source code here: &lt;a href="https://github.com/funkyidol/CameraXSample"&gt;https://github.com/funkyidol/CameraXSample&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;Hope this helps &amp;amp; give me a follow to receive notifications for future articles in this series.&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>camerax</category>
    </item>
    <item>
      <title>Regolith Linux - My descent into Mouse-less navigation</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Mon, 20 Apr 2020 07:12:19 +0000</pubDate>
      <link>https://dev.to/funkyidol/regolith-linux-my-descent-into-mouse-less-navigation-17dc</link>
      <guid>https://dev.to/funkyidol/regolith-linux-my-descent-into-mouse-less-navigation-17dc</guid>
      <description>&lt;h2&gt;
  
  
  What got me interested?
&lt;/h2&gt;

&lt;p&gt;I like to keep things simple and stick to the defaults as much as possible (as much as I would love to customize the hell out of things, sadly I don't have that kind of time nowadays) and after distro-hopping for a while, I was happy to use stock Ubuntu. &lt;/p&gt;

&lt;p&gt;I stuck to the non-LTS release starting from 19.04 and then upgraded to 19.10, but all this while one thing that bothered me a lot was the amount of RAM being consumed by the Gnome Window Manager. It's always fine after a fresh start or reboot at the beginning of the day with around 500-600 MB of usage but by sunset, my usage generally climbs upwards of 1.1 GB. On a 16 GB that's a huge amount especially when my Android IDE, Emulator, Browser and 5 other things are all fighting each other for the already vanishing sweet piece of RAM pie. And also there is this nagging feeling as a developer that there has to be some sort of a memory leak somewhere, because of which this process keeps increasing in size every few hours. At one point I was considering buying more RAM for my laptop just because this was becoming a daily nuisance for me.&lt;/p&gt;

&lt;p&gt;I then started to look for a lightweight Window Managers (WM's) to replace Gnome with something more stable and easy on the memory. There are a lot of excellent options like XFCE and some others, but all of them seemed very outdated in terms of their underlying technology for 2020 (though being more stable at the same time, from what people shared on forums). &lt;/p&gt;

&lt;p&gt;A good friend of mine (and a huge Linux advocate) had suggested me to try out i3wm (a Tiling WM - more on that later) some time back and I quickly dismissed that option at that time because I wasn't convinced about having a keyboard-driven WM. But this time around I gave it a more serious look as I was pretty pissed at Gnome and couldn't wait for the Ubuntu 20.04 update to land.&lt;/p&gt;

&lt;h2&gt;
  
  
  And I discovered Regolith Linux
&lt;/h2&gt;

&lt;p&gt;While giving i3wm a more dedicated research time I found &lt;a href="https://regolith-linux.org/" rel="noopener noreferrer"&gt;Regolith Linux&lt;/a&gt;, which instead of creating a new DE (Desktop Environment) chose to create a collection of open source components bundled in a way to make transition and usage of i3wm easier. Looking at there &lt;a href="https://github.com/regolith-linux/regolith-desktop" rel="noopener noreferrer"&gt;Github Page&lt;/a&gt;, its just a meta package which just holds the issue tracker and release files. It very thoughtfully combines various independent components like i3-gaps WM, Rofi application launcher and notification system, i3bar etc. to create an easy to adopt package, which otherwise takes a lot of time to setup when starting with i3wm. &lt;/p&gt;

&lt;h3&gt;
  
  
  Tiling Window Manager
&lt;/h3&gt;

&lt;p&gt;All this combined makes up for a fairly easy to use &lt;a href="https://en.wikipedia.org/wiki/Tiling_window_manager" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;em&gt;Tiling Window Manager&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;. A Tiling WM is a very unconventional kind of a WM where multiple applications or windows when opened together do not float or overlap each other. They only open beside each other, oriented either vertically or horizontally. No more floating windows, no hidden windows behind one another, no need for a mouse to move windows around. Everything is laid out like Tetris tiles alongside each other. And what do you do when you have too many things open at the same time or you want one of the windows to be full screen, you simply move it to a different 'workspace'. In the following sections, I will talk in more detail about how to get more comfortable with this fundamental shift in the way we use our desktop environment all the while using &lt;strong&gt;just the keyboard&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Installation
&lt;/h2&gt;

&lt;p&gt;Regolith is &lt;a href="https://regolith-linux.org/download/" rel="noopener noreferrer"&gt;available&lt;/a&gt; as both a pre-bundled ISO and a standalone installable PPA. I chose to install it over my current setup as a PPA. &lt;/p&gt;

&lt;p&gt;Use the following commands to install Regolith via PPA.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;add-apt-repository ppa:regolith-linux/release
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;regolith-desktop
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And once installed, restart and Regolith will appear as a desktop session on the login screen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fregolith-linux.org%2Fregolith-screenshot-login.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fregolith-linux.org%2Fregolith-screenshot-login.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you login, you are welcome to this screen with the quick list of keyboard shortcuts to help you start your journey on adopting Regolith. This helper overlay is what makes it so easy to start using the i3wm.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fregolith-linux.org%2Fscreenshot-remontoire.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fregolith-linux.org%2Fscreenshot-remontoire.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting Familiar
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Super&lt;/code&gt; key is your best friend in this desktop environment. Everything that you wish to do all starts with the &lt;code&gt;Super&lt;/code&gt; key. To show or hide this keymap overlay just press &lt;code&gt;Super + Shift + ?&lt;/code&gt;&lt;br&gt;
To launch an application, just press &lt;code&gt;Super + Space&lt;/code&gt; to start the launcher app&lt;/p&gt;

&lt;p&gt;I won't list all the keyboard shortcuts here which are readily available in the help menu. Rather I would talk about some common usage patterns and how to quickly get into the habit of using those patterns.&lt;/p&gt;

&lt;p&gt;But before that, let's get a little more familiar with the environment itself.&lt;/p&gt;
&lt;h3&gt;
  
  
  Workspaces
&lt;/h3&gt;

&lt;p&gt;On the bottom left is our workspace indicators. The workspaces are how we spread our windows around in this system instead of stacking them over each other. You start with only one workspace when you login but as you move windows around to the workspaces, they become active. The workspaces might be numbered but it's not required to use them in sequential order. You can use whichever workspaces at any given point as per your comfort. The highlighted workspace indicator will tell you which workspace is currently active.&lt;/p&gt;
&lt;h3&gt;
  
  
  App Launcher
&lt;/h3&gt;

&lt;p&gt;Regolith bundles the 'Rofi Application Launcher' which is a type-to-search type of a launcher. Just start typing what you are looking for and it will immediately filter results based on the text. And as you use this launcher more and more it starts remembering your choices and starts showing them on first like in the image below &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxsrktufqm5w32ms7ppt2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fxsrktufqm5w32ms7ppt2.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;One thing to remember here is that 'restart' is actually 'reboot' here so it might be something to look for when starting out.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  Notifications
&lt;/h3&gt;

&lt;p&gt;Notifications is probably the most not-so-obvious feature in Regolith. Regolith bundles with the &lt;a href="https://regolith-linux.org/docs/interface/notifications/" rel="noopener noreferrer"&gt;Rofication Notification System&lt;/a&gt; which takes a very bold decision of never showing an alert for your notifications, making the setup distraction-free. All new notifications received go on to incrementing a counter near the bottom right of the i3bar. There is still a notification sound but no visible UI alerts. To see the notification you press &lt;code&gt;Super + n&lt;/code&gt; to open a rofi window showing all  the notifications. This is something I like a lot since it helps alleviate visual distractions while I'm doing deep work.&lt;/p&gt;

&lt;p&gt;Now the other big thing with these notifications is that they are non-actionable. You cannot click or select this notification to open the respective app which furnished the notification. For me, this needed getting used to as with the traditional notifications system we are in the habit of taking actions directly on the notification to open the respective application. But on a personal level, I am fine with it since most of the notification I receive are from Slack or Android Studio. &lt;br&gt;
To delete these notifications simply press &lt;code&gt;Delete&lt;/code&gt; to remove individual notifications or &lt;code&gt;Shift + Delete&lt;/code&gt; to delete all notifications from a particular app.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0bur9hlikf0h0d9hd2b8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F0bur9hlikf0h0d9hd2b8.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Traditional Gnome Settings
&lt;/h3&gt;

&lt;p&gt;One good thing about Regolith, which is not available in the standard i3wm setup is the availability of the traditional gnome settings manager to allow you easy access to system settings without the need of relearning anything here. Its implemented as normal floating windows easy to interact with using a mouse.&lt;/p&gt;
&lt;h3&gt;
  
  
  Navigation  and Usage Patterns
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Working with non-tillable windows&lt;/strong&gt;&lt;br&gt;
There are a few applications which work well in the tiling paradigm and can lead to some weird window placements and artifacts. This happens for me notably with the Android Emulator.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa874rgeq91btz0mhoymy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fa874rgeq91btz0mhoymy.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
Luckily we do have an option to make any window a floating window. Pressing &lt;code&gt;Super + Shift + F&lt;/code&gt; on the currently selected window makes if a floating one which then overlaps the rest of the environment.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2qcx29ltnw3dc7d6l0k2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F2qcx29ltnw3dc7d6l0k2.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
But we can't simply move this window around with the mouse. Here once again we have to take help from our friend the &lt;code&gt;Super&lt;/code&gt; key and keep it pressed to enable window dragging behavior while you move the window around using the mouse.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Setting up a workflow&lt;/strong&gt;&lt;br&gt;
The AHA moment truly came to me when I created a workflow for myself and realized how easy it is to do pretty much everything with just the keyboard. To start setting up a workflow first we have to understand that all the applications that we will be using will live on separate workspaces. So basically you have to plan your workspaces depending on the &lt;strong&gt;priority and frequency&lt;/strong&gt; of the apps which are in play. &lt;br&gt;
For me the browser is the go-to app, so Firefox lives in the Workspace-1. Next is Android Studio which goes in Workspace-3 and next Slack which goes in Workspace-5. I chose Workspace-3&amp;amp;5 because that way my most important apps are all on odd workspace numbers. On Workspace-2, I have my Notion app and Gitlab app in vertical split mode. All music players go on Workspace-4 and anything extra goes in Workspace-6. I have had to rarely use more than 6 workspaces at a given time. In the customization section, I will talk about how to automatically move windows to these workspaces at launch.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Working parallelly in multiple apps&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;As I mentioned earlier, in this tiling desktop environment, apps open along with each other. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7mixho0vpbia8jpu93sw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7mixho0vpbia8jpu93sw.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
When more than one app are open, they always show up dividing the currently available space in half either vertically or horizontally. By default, the apps always open split vertically side-by-side. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgi2r9clrctfkfb0pz9g4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fgi2r9clrctfkfb0pz9g4.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
So the first window shows of full screen, second split it in half, the third app will split the screen in one-thirds and so on. &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqfxg6emoan9mw23af9bw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fqfxg6emoan9mw23af9bw.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
But instead, if I wish to open the third app split in the lower half of the right side split horizontally, I will just press the orientation modifier i.e. &lt;code&gt;Super + Backspace&lt;/code&gt; before opening the third app. And not when I open the third app its shown like in the image below.&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7s8evy2in8a8qyu1q69g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F7s8evy2in8a8qyu1q69g.png" alt="Alt Text" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
The power of Regolith shines especially when you have multiple apps open in the same workspace aligned in the grid pattern of your choice. To move focus between the various windows, instead of moving the mouse, you can very quickly press &lt;code&gt;Super + &amp;lt;arrow keys&amp;gt;&lt;/code&gt; to focus on the app depending on the window layout. &lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Moving windows between workspaces&lt;/strong&gt;&lt;br&gt;
This point is more about adopting a new habit more than anything else. To move the currently selected window to a different workspace we press &lt;code&gt;Super + Shift + &amp;lt;workspace number&amp;gt;&lt;/code&gt;. This moves the current window to the desired workspace while keeping you on the workspace you were on. So if I wanted to move my terminal window from Workspace-1 to 2 but while remaining on Workspace-1 then I press &lt;code&gt;Super + Shift + 2&lt;/code&gt;. But If I want to 'take' the terminal window 'with me' to Workspace-2 then I press &lt;code&gt;Super + Alt +2&lt;/code&gt;, This moves the terminal window to Workspace-2 and also makes it the active workspace.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Resizing windows&lt;/strong&gt;&lt;br&gt;
One behavior I was not expecting to be this easy to do without a mouse was resizing windows. Pressing &lt;code&gt;Super + r&lt;/code&gt; starts the window edit mode where pressing 'left' or 'right' resizes the split of horizontal windows and 'up' or 'down' resizes the split for vertical windows. You can also press '+' or '-' to increase or decrease margin between the windows. With a contrasting wallpaper, it looks pretty cool. Press &lt;code&gt;Esc&lt;/code&gt; or &lt;code&gt;Super + r&lt;/code&gt; again to exit the edit mode.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Customization
&lt;/h2&gt;

&lt;p&gt;Though I was pretty happy with the default configuration that Regolith provides for i3, there were a few things I still wanted to change to help make things easier.&lt;/p&gt;

&lt;p&gt;But before starting any customization we first have to setup the i3 config file to modify it. Best way to do so is to  follow the steps in &lt;a href="https://regolith-linux.org/docs/howto/stage-configs/" rel="noopener noreferrer"&gt;this link&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Auto open apps in specific workspaces&lt;/strong&gt;&lt;br&gt;
After you have staged the i3 config files in the path &lt;code&gt;/home/&amp;lt;user&amp;gt;/.config/regolith/i3&lt;/code&gt; modify the config file and append the following in the end.&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;for_window&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Slack&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;move&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;workspace&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;ws5&lt;/span&gt;
&lt;span class="n"&gt;for_window&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;jetbrains-studio&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;move&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;workspace&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;ws3&lt;/span&gt;
&lt;span class="n"&gt;for_window&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ICE-SSB-notion&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;move&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;workspace&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;ws2&lt;/span&gt;
&lt;span class="n"&gt;for_window&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;ICE-SSB-gitlab&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;move&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;workspace&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;ws2&lt;/span&gt;
&lt;span class="n"&gt;for_window&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Rhythmbox&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="n"&gt;move&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;workspace&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;ws4&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;The above code will always move my Slack window to Workspace-5, Android Studio to Workspace-3 and so on. &lt;br&gt;
&lt;em&gt;Hot Tip:&lt;/em&gt; If you want to find the name of the window to be included in the above code, just open a terminal and type &lt;code&gt;xprop&lt;/code&gt; and then click with your mouse on the desired window. It will print a lot of window-related internal details in the terminal and you can pick up the window name from there.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Show RAM usage in bottom bar&lt;/strong&gt;&lt;br&gt;
By default the bottom i3 bar shows everything except the RAM usage which is still super awesome because most desktop environments don't show resource usage by default and you have to go hunting for an extension or app to do so. So to enable the RAM usage indicator, go to &lt;code&gt;/home/&amp;lt;user&amp;gt;/.config/regolith/i3xrocks&lt;/code&gt; and open the config file and uncomment the following lines&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;memory&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="n"&gt;interval&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;Now just type &lt;code&gt;regolith-look refresh&lt;/code&gt; in the terminal to show the small pie chart of the total RAM with the available RAM amount.&lt;/p&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;p&gt;&lt;strong&gt;Add a "Clipboard Manager"&lt;/strong&gt;&lt;br&gt;
This was the &lt;strong&gt;most&lt;/strong&gt; tricky thing to get working in Regolith. As a daily user of Gpaste, clipboard manager is part of my regular flow of working when copy-pasting code from Stack Overflow on to my project and because there is no default support for traditional pop-up window systems, Gpaste didn't work. After a lot of Googling and hit and trials, I landed on &lt;a href="https://github.com/erebe/greenclip" rel="noopener noreferrer"&gt;Greenclip&lt;/a&gt;. This a rofi based clipboard manager which integrates which shows the previous selections similarly to how the notifications are shown.&lt;/p&gt;

&lt;p&gt;To install, just download the executable from the github page and add the following file to the Regolith config file&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Greenclip
&lt;/span&gt;&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;greenclip&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;to&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="nb"&gt;file&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;greenclip&lt;/span&gt;
&lt;span class="n"&gt;bindsym&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;mod&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;Shift&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;rofi&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;modi&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;clipboard:$greenclip print&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;show&lt;/span&gt; &lt;span class="n"&gt;clipboard&lt;/span&gt;
&lt;span class="k"&gt;exec&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;no&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;startup&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;greenclip&lt;/span&gt; &lt;span class="n"&gt;daemon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;You can check out the video linked in the Greenclip Github page readme for detailed step by step instructions.&lt;/p&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  Not everything is perfect
&lt;/h2&gt;

&lt;p&gt;Frankly, I don't have much to write in this section apart from the fact that using Regolith definitely had its own learning curve but not as much as a vanilla i3wm setup would be. It's obviously about what clicks for whom and putting in just the little bit of effort to becoming more productive and feel like a "Hacker Man". &lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7f1otodkw3fkpbw3itz9.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7f1otodkw3fkpbw3itz9.gif" width="480" height="270"&gt;&lt;/a&gt;&lt;br&gt;
Though I'm still learning the ropes of using more keyboard shortcuts to get a better window layout, but all that I feel will come with time as its need would move up from good-to-have to must-have&lt;/p&gt;

&lt;h2&gt;
  
  
  Future
&lt;/h2&gt;

&lt;p&gt;Though I'm super happy with my setup right now, I find the &lt;a href="https://www.omgubuntu.co.uk/2020/03/pop-shell-wants-to-bring-proper-tiling-window-features-to-gnome-shell" rel="noopener noreferrer"&gt;Pop Shell&lt;/a&gt; extension from System76 to be really interesting. This extension will bring the goodness of tiling WM to the standard Gnome setup which will make it further easier to use for first time adopters. But I still can't shake the feeling that using Gnome will still be a lot more resource hungry compared to the minimalistic i3wm via Regolith. None the less I'm also looking forward to the new developments and releases of Regolith Linux and if possible consider a donation to the developers.&lt;/p&gt;

</description>
      <category>linux</category>
      <category>gnome</category>
      <category>i3wm</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Effective logging in Production with Firebase Crashlytics</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Thu, 25 Jul 2019 07:12:21 +0000</pubDate>
      <link>https://dev.to/funkyidol/effective-logging-in-production-with-firebase-crashlytics-m27</link>
      <guid>https://dev.to/funkyidol/effective-logging-in-production-with-firebase-crashlytics-m27</guid>
      <description>&lt;p&gt;Logging and specifically crash logging is a very important aspect of any app that is available beyond a developers machine. Its simply the act of getting the internal application details generated at the time of the application crash. Be it available to testers or available for public use, catching crashes and identifying errors is critically important for developers to provide a quality product. In mobile app development, Firebase Crashlytics is one of the most widely used Crash logging tool. Crashlytics was build by Fabric (Owned by Twitter) and was later bought by Google and integrated within Firebase.&lt;/p&gt;

&lt;p&gt;At Envision, we heavily rely on Crashlytics to give us the insight into our app quality and any issues our users might be facing while using our app. With Crashlytics, we get the crashes our users face, as well as any internal issues our app might be running into which are not necessarily visible to end users.&lt;/p&gt;

&lt;h3&gt;
  
  
  Goals
&lt;/h3&gt;

&lt;p&gt;The goals we have with Crashlytics currently are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Crash Logging&lt;/li&gt;
&lt;li&gt;Issue Identification &amp;amp; Diagnosis&lt;/li&gt;
&lt;li&gt;App Behaviour insight&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Implementation
&lt;/h3&gt;

&lt;p&gt;There are various features in Crashlytics that generally go unnoticed. We would like to highlight some of the ways we use Crashlytics to achieve the aforementioned goals. &lt;/p&gt;

&lt;p&gt;P.S. - The following details are Android specific but can be easily translated for iOS. All the links below also contain the steps and details of implementing Crashlytics in iOS&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic Crash Logging:
Part of the core feature set, automatic crash logging is what you get the moment you integrate Crashlytics in your project. You can find the integration steps &lt;a href="https://firebase.google.com/docs/crashlytics/get-started?platform=android"&gt;here&lt;/a&gt;. If you want to exclude debug builds from polluting your crash logs during active development, you can follow the steps &lt;a href="https://firebase.google.com/docs/crashlytics/customize-crash-reports?platform=android#enable_opt-in_reporting"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Non-Fatal Logging:&lt;br&gt;
Non-Fatal logging is where we start using the advanced features of Crashlytics which helps in diagnosing and fixing issues beyond the crashes logged while the app was being used. When exceptions are logged manually in Firebase, they are logged as non-fatals. The best way to use non-fatals in your app is to log all exceptions generated in the catch section of the try/catch block. Additionally, we also create exceptions and log them in situations where our app is in an undesirable state and needs to be examined for the cause and determine its effect on the app.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;try {
      methodThatThrows()
  } catch (e: Exception) {
      Crashlytics.logException(e)
      // handle your exception here
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Debug Logging:&lt;br&gt;
We use Timber for managing logs on Android and it makes it very easy for us to manage our logging in debug &amp;amp; production versions. The following configuration prints the logs to the console in the debug version, but in the production versions, it sends the debug logs along with the exception to Firebase.&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;override fun onCreate() {
      super.onCreate()
         if (!BuildConfig.DEBUG) {
             Fabric.with(this, Crashlytics())
             Timber.plant(CrashReportingTree())
         } else {
             Timber.plant(Timber.DebugTree())
         }
   }

   private inner class CrashReportingTree : Timber.Tree() {
      override fun log(priority: Int, tag: String?, message: String, throwable: Throwable?) {
         if (priority == Log.ERROR || priority == Log.DEBUG) {
            Crashlytics.log(priority, tag, message)
            if (throwable != null) {
               Crashlytics.logException(throwable)
            }
         } else return
      }
  }
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;User Id/Email tagging:&lt;br&gt;
Firebase has the ability to attach the logs collected from a particular user to their unique UserId. This allows us to diagnose and fix issues reported to us by our users especially during the beta test phase. In Firebase console, we have the ability to filter crashes and non-fatal logs for a particular user.&lt;br&gt;
&lt;code&gt;Crashlytics.setUserIdentifier(userId)&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;App Versioning&lt;br&gt;
At Envision, we try to follow proper versioning of our beta and release builds so that all the crashes and logs collected in Firebase can be quickly filtered for a specific version and fixed for the next version. This same scheme is followed for the internal test builds as well so that additional data could be logged exclusively to the test versions.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Bottlenecks
&lt;/h3&gt;

&lt;p&gt;Even with all these advanced features, Firebase still falls short is some occasions.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Limited log collection storage
Debug Log storage is limited to 64KB per user in firebase and older logs are deleted as new logs come in. This can be a big hindrance in issue diagnostic since many at times by the time we identify an issue and begin diagnostics, the logs are gone.&lt;/li&gt;
&lt;li&gt;Limited filtering options
The number of filtering options in Firebase is limited to app versions, crashes vs. non-fatals, and User Id. All these work fine for basic crash log usage but for more complex and rare issues these filtering options are restricting. There are no ways in which logs can be filtered out based on a specific OEM, device category or device model. This is especially useful for Android apps where the number of available devices and OEM's are huge and device specific issues are common.&lt;/li&gt;
&lt;li&gt;No search functionality
There is no search functionality in Firebase crashlytics to search for a specific crash or debug log which again becomes very essential when dealing with specific issues or diagnosing rare crashes.&lt;/li&gt;
&lt;li&gt;Slow interface
Using Firebase console overall feels very slow and bloated with very large page loading and rendering times. And that is when you are using the Chrome browser. I become much slower when using Firefox.&lt;/li&gt;
&lt;li&gt;No custom alerts
Firebase is very limited when it comes to issues or trend alerts. They only send email alerts for new issues, regression issues, and trending issues. There are no ways to create custom alerts for e.g. alerts for trends for a specific issue or specific device.&lt;/li&gt;
&lt;li&gt;No way to directly see debug logs
Currently, the only way to see debug logs os to find them inside a crash log or a non-fatal crash. We understand that the way debug logs are collected and associated on a per crash/non-fatal basis but there should be ways for us to traverse through debug logs on a per User Id or device model basis as well.&lt;/li&gt;
&lt;li&gt;Limited dashboard &amp;amp; graph statistics
There is only one usable graph that is presented inside Firebase Crashlytics and that is the total crash count on a day, week or month basis. There should be more ways for us to visualize the crash statistics as well. Fabric had very useful graphs on their dashboards that gave a lot of value without us to dig around for that info.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Going Forward
&lt;/h3&gt;

&lt;p&gt;As our app matures and our use case, Crashlytics has come to a point where we are getting restricted on a regular basis in achieving our goals to quickly identify and resolve issues. We are now looking at some other 3rd party &amp;amp; more advanced crash logging solutions such as &lt;a href="//notion://www.notion.so/letsenvision/bugsnag.com"&gt;BugSnag&lt;/a&gt; which might be paid but add value to our product in terms of reaching higher quality.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;originally posted at &lt;a href="https://medium.com/letsenvision/effective-logging-in-production-with-firebase-crashlytics-9fb3af9f242d"&gt;https://medium.com/letsenvision/effective-logging-in-production-with-firebase-crashlytics-9fb3af9f242d&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>android</category>
      <category>ios</category>
      <category>crashlytics</category>
      <category>logging</category>
    </item>
    <item>
      <title>Dual booting Ubuntu Budgie on Windows laptop</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Wed, 10 Jul 2019 04:55:49 +0000</pubDate>
      <link>https://dev.to/funkyidol/dual-boosting-ubuntu-budgie-on-windows-laptop-4a55</link>
      <guid>https://dev.to/funkyidol/dual-boosting-ubuntu-budgie-on-windows-laptop-4a55</guid>
      <description>&lt;p&gt;My thoughts on the process of installing Ubuntu Budgie in parallel to Windows 10 on a Dell Inspiron 15 7000 series laptop.&lt;/p&gt;

&lt;p&gt;Preconfigured Dell windows machine don't make it easy for you to dual boot Linux. I really had to mess around in BIOS to make it work.&lt;/p&gt;

&lt;p&gt;Steps that I followed:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Disable secure boot.&lt;/li&gt;
&lt;li&gt;Change boot order&lt;/li&gt;
&lt;li&gt;Change SSD config from RAID to AHCI (&lt;a href="https://www.youtube.com/watch?v=9ngnIKqPOc4"&gt;https://www.youtube.com/watch?v=9ngnIKqPOc4&lt;/a&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All of this creates a lot of friction and had to Google around a lot to get to this point which is not really idle for new users and experienced people alike.&lt;/p&gt;

&lt;p&gt;My inbuilt nVidia GPU gave me a very hard time with the whole install process. My machine went blank everytime I logged into Linux. I initially couldn't understand what was happening &amp;amp; reinstalled Linux multiple times just to make sure the installation was fine.&lt;/p&gt;

&lt;p&gt;Again after a lot of googling I figured out that its a nVidia driver issue so I had to&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Go to command-line before login&lt;/li&gt;
&lt;li&gt;Setup my wifi from CLI&lt;/li&gt;
&lt;li&gt;Add the repo for drivers, Install &amp;amp; configure the drivers from CLI
And all this on a 4k display without font scaling, so literally reading ants there.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;And this whole situation can only be fixed if distros ignore nVidia hardware unless drivers are installed bcoz Distros can't bundle the proprietary drivers due to licensing issues.&lt;/p&gt;

&lt;p&gt;After successfully installed and working, few things I'm missing from windows in Ubuntu Budgie&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Factional display scaling. It's either 100% or 200%. Nothing in between&lt;/li&gt;
&lt;li&gt;Native apps for Notion, Google Drive backup &amp;amp; sync. 
But this list used to a lot longer 4-5 yrs ago so thats a very good sign of how much the ecosystem has evolved over the years.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;All this is just the initial thoughts. They will evolve I'm sure. Overall things are fine. What my expectation out of this move is a secure &amp;amp; a dev friendly work environment and better compilation speeds. I'm still not holding my breath for improved battery life though.&lt;/p&gt;

&lt;p&gt;Edit:&lt;br&gt;
Received some help from my twitter friends which shared the following links to help out with some of the issues&lt;br&gt;
&lt;a href="https://www.omgubuntu.co.uk/2019/06/enable-fractional-scaling-ubuntu-19-04"&gt;https://www.omgubuntu.co.uk/2019/06/enable-fractional-scaling-ubuntu-19-04&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.omgubuntu.co.uk/2019/02/odrive-google-drive-linux-client"&gt;https://www.omgubuntu.co.uk/2019/02/odrive-google-drive-linux-client&lt;/a&gt;&lt;br&gt;
&lt;a href="https://www.omgubuntu.co.uk/2019/05/slimbook-battery-optimizer-ubuntu"&gt;https://www.omgubuntu.co.uk/2019/05/slimbook-battery-optimizer-ubuntu&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A helpful link I found: &lt;a href="https://medium.com/@pwaterz/how-to-dual-boot-windows-10-and-ubuntu-18-04-on-the-15-inch-dell-xps-9570-with-nvidia-1050ti-gpu-4b9a2901493d"&gt;https://medium.com/@pwaterz/how-to-dual-boot-windows-10-and-ubuntu-18-04-on-the-15-inch-dell-xps-9570-with-nvidia-1050ti-gpu-4b9a2901493d&lt;/a&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>Why becoming &amp; staying productive in India is difficult</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Mon, 03 Jun 2019 06:19:56 +0000</pubDate>
      <link>https://dev.to/funkyidol/why-becoming-staying-productive-in-india-is-difficult-3b27</link>
      <guid>https://dev.to/funkyidol/why-becoming-staying-productive-in-india-is-difficult-3b27</guid>
      <description>&lt;p&gt;People talk a lot about developer productivity but some points that people don't talk about are the environmental issues, available options, and pricing at which the tools are available to the developers in a particular country. Building &amp;amp; maintaining a productive work environment as an independent developer in India is very costly. So costly that on average we pay 2 to 3 times &amp;amp; sometimes even higher for the same hardware, software, and accessories than what people have to pay outside India.&lt;/p&gt;

&lt;p&gt;But it's not just the price but many other factors that I want to bring to your attention:&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Issues
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Costly Hardware:&lt;/strong&gt; General cost of hardware (laptops/computers) and their accessories (monitors/keyboard/mice) are much higher in India compared to the US or East Asian market. My current laptop (Dell 7570) which I procured from the US cost me 25% lesser than what was available in India. 
My recent research into external monitor pricing also revealed a difference of good 30% to 50% between various models. And all these differences are direct market pricing and don't even consider the various deals that can increase the difference to approx double.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Subpar Configuration:&lt;/strong&gt; The configuration that is available in India for laptops, computers, monitors, etc, for the most part, can be categorized as aftermarket leftover. The kind of configurations that are offered specially for the pre-build machines like laptops is bare-bones at a price point which will generally fetch a medium level config in other markets. And even the configurations which are supposedly "maxed out" have limited RAM, SSD, or Display options. For e.g. the laptop I own now has a 4K display (which I got from the US) was not even an option in any of the configurations available in India. And again going back to the first point, the prices at which these machines are offered are way more than the maxed out options in other countries. Similarly, for monitors, the specs with which they are available in India are generally many generations old but we still have to pay the full price that is of the current generation one. &lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limited Companies &amp;amp; options:&lt;/strong&gt; The number of companies that are actively selling their products are just limited to very high-end brands. It's a bit contrasting in nature when compared to the mobile industry where the devices are launched in India as soon as they are announced but for computer hardware peripherals we have to wait for months and sometimes years for products to start selling locally. Sometimes they are never even bought to the Indian market altogether. Logitech, Razer, and other such peripheral companies are another example where they don't offer most of the range of their devices in the Indian market.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Non-existent Customer Support:&lt;/strong&gt; The idea of customer support is a joke in India. Anytime you have to deal with customer support for most of the organizations, you can very well kiss your product goodbye. Apart from the handful of companies like Logitech with which I have had good experience with, most companies just fail to deliver on the concept of customer support. Whereas in the US I have heard of easy exchange or repair policies in case the hardware faces any issue, no such policy exists in India. At max, they will offer to repair your device at prices that are close to the price of the original device and take many months to repair, that too when your product is under warranty. And even if you bought your product outside India which on paper has an International warranty, customer support will deny any help citing unavailability of the configuration in India. In the end, even if you are willing to pay money for a product, keeping it alive for a natural lifespan of the product is solely based on luck.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Taxes:&lt;/strong&gt; With the new GST (Goods &amp;amp; Service Tax) introduced in India in 2017 coupled with the hefty and ever-increasing import duties levied on most of the products and peripherals, we end up paying upwards of 25% of the actual price in taxes. This includes both Hardware &amp;amp; Software.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No deals or discounts:&lt;/strong&gt; Indian markets don't offer the kind of deals the US or other countries might enjoy on a regular basis. And especially no Black Fridays here as well. This limits a lot of opportunities for Indian consumers to buy discounted peripherals. It's not that we don't have deals per se but when the so-called Amazon Lightning deals hover in the range of 10% discounts, it doesn't make it the most lucrative of offers.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Environmental Issues
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Dust &amp;amp; Heat:&lt;/strong&gt; Geographically, most of India has summers or rather hot temperatures for close to 9 months in a year topping at a good 50 degrees Celsius. This makes cooling a big challenge, especially for laptops and computers. And the more we have to cool the devices artificially, the more we have to deal with DUST. Dust is one of the biggest reasons for the small lifespan of electronic products here. Laptops &amp;amp; computers face the biggest challenge since they get clogged very frequently with dust and have to be cleaned regularly in order to keep them operational. Mice have it the worst when it comes to dust. Because when dust gets inside the mouse, it accumulates inside the small click mechanism of the buttons and then they start misfiring or getting stuck. Until the time I was using the regular Logitech Mouse I had to buy a new one every year since they wouldn't last more than that. The latest mouse I own is a Logitech G603, which has a 3-year warranty and I have already got it exchanged once because of the dust issue.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Difficult to create Home Offices:&lt;/strong&gt; Room sizes in Indian Homes are generally very small and keeping a full desk with a complete external monitor setup in a regular bedroom is out of the question. And so is having a separate room for your home office. And that's because Indian homes don't have a concept of centralized cooling/heating. So every room in the house needs to be individually cooled with separate air conditioning units. And thanks to the high electricity prices, cooling multiple rooms in the house at the same time becomes financially unviable. The only option that we have is to work on our laptops and adjust between working on bed or small temporary tables.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I understand that there might be business reasons for many of the points I have shared above but at the end the fact is that we are deprived of the tools which increase the quality and the quantity of the work we put out and unless these companies start selling their products and advertise them, how can they expect to grow in one of the world's largest markets. I also understand that the government rules for entering the market and selling products in India are very difficult to work with. &lt;br&gt;
For environmental reasons, there is obviously nothing that can be done and creating a setup that counters these factors are more a matter of sunk costs and high running costs.&lt;/p&gt;

&lt;p&gt;I hope that companies around the world who work with India based Freelance Developers have some insight into issues we face that might not look apparent but are our reality with which we have to deal with. And if they really care about their productivity, they can help contribute to it by buying the required hardware and peripherals for them, as they would for an on-premise employee.&lt;/p&gt;

&lt;p&gt;P.S. I am aware that there can be many counter-arguments to the points I have shared but do understand that they come from my personal experience of working remotely for the past 5 years from Delhi, in conditions that might be very personal to me.&lt;/p&gt;

</description>
      <category>productivity</category>
      <category>rant</category>
      <category>india</category>
    </item>
    <item>
      <title>Novices guide to Dependency Injection &amp; Dagger2 - Part 2 - Benefits</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Sun, 21 Apr 2019 04:26:35 +0000</pubDate>
      <link>https://dev.to/funkyidol/novices-guide-to-dependency-injection-dagger2-part-2-benefits-3gop</link>
      <guid>https://dev.to/funkyidol/novices-guide-to-dependency-injection-dagger2-part-2-benefits-3gop</guid>
      <description>&lt;p&gt;In the second article of the 'Novices guide to DI', before we go into further details, lets talk about the benefits. &lt;/p&gt;

&lt;p&gt;Why would we event want to get into the complexity of understanding DI?&lt;br&gt;
What advantages do we have by changing the way we code as discussed in the previous post: &lt;/p&gt;
&lt;div class="ltag__link"&gt;
  &lt;a href="/funkyidol" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F899%2F4b30062c-9a9f-4e4c-9ccc-8794cb5038d2.jpg" alt="funkyidol"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/funkyidol/novices-guide-to-dependency-injection--dagger2---part-1-ga5" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;Novices guide to Dependency Injection &amp;amp; Dagger2 - Part 1 - Introduction&lt;/h2&gt;
      &lt;h3&gt;Kshitij Aggarwal ・ Feb 18 '19&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#android&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#dagger&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#beginners&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I believe that knowing the advantages of learning a concept gives an extra dose of motivation to break past the mental block and step outside the comfort zone and to reach a place where the complexity fades away and things become more clear.&lt;/p&gt;

&lt;p&gt;Lets begin:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;em&gt;Decoupling&lt;/em&gt;:
As discussed in the previous post of the series, the whole idea of DI is to segregate the place where an object is created and where it is consumed. This decoupling is what allows for all the benefits discussed below.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Reusability&lt;/em&gt;:
As we now have decoupled the object creation part and shifted it away from the actual code where it gets used, we can now easily reuse it without having to write the object creation code everytime it is required in multiple places. Simply consider things like error dialogs or toast messages. If we write the dialog or toast creation code once, we dont have to write it again everytime you want to use them. Just 'inject' the object and use them.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Maintainability&lt;/em&gt;:
Now if you explore this possibility of having all object creation code, which is written only once throughout the code base and getting re-used everywhere, how easy it would be to perform any sort of changes (for bug fixing or new features or changed functionality) in this one place and have it gets reflected everywhere automatically without having to change it again everywhere the object was being used. This reduces the number of bugs introduced due to mismatched or improper object creation.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Testability&lt;/em&gt;: 
Further benefits and probably the most important one, thanks to the object creation centralization and decoupled object usage is testing. Since we now have two different entities - the object creation and the other it's usage, we can now test both these entities independently giving us a more robust overall application. We can now test the object creation by feeding it to a test code which verifies whether the created object is working fine or not. At the same time, we can test the object consumption code by feeding it with test objects of various permutations and combinations and test how our code behaves under various conditions.
I will not go much into detail on how to actually perform these testes as this is a slightly advanced topic for now.&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Increased development speed&lt;/em&gt;:
Last but the most incrementally beneficial of the advantages is the increased development speed. Sure, in the starting of a project it might feel that its taking longer to setup the whole method injection or constructor injection but as you get into the habit, it becomes much faster. And as the application size grows, the benefits of DI starts showing as the time spent of creating objects repeatedly goes away as you confidently provide the objects to classes where they are needed.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So these are some of the benefits we get when we actually jump into the world of DI. There are even further benefits to be discussed but those are more specific to using the Dagger library itself, so those I will discuss in a separate post.&lt;/p&gt;

&lt;p&gt;In the next post we will start with looking at Dagger and how we can use it to simplify DI&lt;/p&gt;

</description>
      <category>android</category>
      <category>dagger</category>
      <category>di</category>
      <category>beginners</category>
    </item>
    <item>
      <title>Novices guide to Dependency Injection &amp; Dagger2 - Part 1 - Introduction</title>
      <dc:creator>Kshitij Aggarwal</dc:creator>
      <pubDate>Mon, 18 Feb 2019 08:41:25 +0000</pubDate>
      <link>https://dev.to/funkyidol/novices-guide-to-dependency-injection--dagger2---part-1-ga5</link>
      <guid>https://dev.to/funkyidol/novices-guide-to-dependency-injection--dagger2---part-1-ga5</guid>
      <description>&lt;p&gt;This guide is an attempt to convert the difficult to understand concept of Dependency Injection into a language which is easier to understand and comprehend. This article draws all the wisdom from hundred's of sources I read to wrap my head around this concept and from using it in production application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Defining Dependency Injection (DI)
&lt;/h2&gt;

&lt;p&gt;In very simple terms, &lt;em&gt;DI is trying to segregate the dependency creation and dependency calling&lt;/em&gt;. &lt;/p&gt;

&lt;p&gt;Let me explain.&lt;/p&gt;

&lt;p&gt;Dependency is any class or object that is required and is not available in the current scope. It could be a platform level class like &lt;code&gt;Toast&lt;/code&gt;, a 3rd party library like &lt;code&gt;Retrofit&lt;/code&gt; or your own class defined somewhere else in the project. Currently, every time you require any of these 'dependencies', you try to create its object using the &lt;code&gt;new&lt;/code&gt; operator. Alternatively, you may try to use some design pattern like Singleton or Factory to call upon a preexisting object of the required dependency.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ProfessionalDetails&lt;/span&gt; &lt;span class="n"&gt;pDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ProfessionalDetails&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;AcademeicDetails&lt;/span&gt; &lt;span class="n"&gt;aDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AcademicDetails&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;But now let me ask you to change the way you do things a little differently&lt;/em&gt;. Instead of creating or accessing your dependencies directly, how about I tell you that all the dependencies you ever require are always provided to you via the method parameters and you never have to create a new object yourself. The above code will then look something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;ProfessionalDetails&lt;/span&gt; &lt;span class="n"&gt;pDetails&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
   &lt;span class="kd"&gt;private&lt;/span&gt; &lt;span class="nc"&gt;AcademeicDetails&lt;/span&gt; &lt;span class="n"&gt;aDetails&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;

   &lt;span class="cm"&gt;/* Example of Method (Dependency)Injection */&lt;/span&gt;
   &lt;span class="kd"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;userDetails&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;ProfessionalDetails&lt;/span&gt; &lt;span class="n"&gt;pDetails&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;AcademicDetails&lt;/span&gt; &lt;span class="n"&gt;aDetails&lt;/span&gt;&lt;span class="o"&gt;){&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;pDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pDetails&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;aDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;aDetails&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;    
   &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is called &lt;strong&gt;Method  (Dependency)Injection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;As we defined DI earlier as segregating the dependency creation and calling, here, you are not worried where or how the dependency is created, you are just concerned with using them. Similarly if any dependency you require at Class level is provided to you via the class constructor like shown below, it's called &lt;strong&gt;Constructor Dependency Injection&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now as a first step, I implore all of my readers to stay with this thinking of not creating new dependency objects and to get the dependencies via method or constructor injection. And to take things a little further, if I can ask you reduce the use of class variables in your methods and try to pass the requirements via method parameters. This is something which will be useful when we look at the larger picture of testable code but more on that later.&lt;/p&gt;

</description>
      <category>android</category>
      <category>dagger</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
