<?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>Forem: Subbu Lakshmanan</title>
    <description>The latest articles on Forem by Subbu Lakshmanan (@subbramanil).</description>
    <link>https://forem.com/subbramanil</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%2F18587%2Fca1b8a36-25ff-4939-9a0f-0a43d8c586e4.JPG</url>
      <title>Forem: Subbu Lakshmanan</title>
      <link>https://forem.com/subbramanil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/subbramanil"/>
    <language>en</language>
    <item>
      <title>Suspending functions should not be called on a different dispatcher</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Mon, 14 Aug 2023 05:56:46 +0000</pubDate>
      <link>https://forem.com/subbramanil/suspending-functions-should-not-be-called-on-a-different-dispatcher-1a7</link>
      <guid>https://forem.com/subbramanil/suspending-functions-should-not-be-called-on-a-different-dispatcher-1a7</guid>
      <description>&lt;h3&gt;
  
  
  Why
&lt;/h3&gt;

&lt;p&gt;There seems to be confusion around using Dispatchers in Coroutines. Dispatchers are used to mention the thread on which the coroutine should run.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Dispatchers.Main&lt;/code&gt; - Main thread&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Dispatchers.IO&lt;/code&gt; - IO thread&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Dispatchers.Default&lt;/code&gt; - CPU-intensive thread&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;While we should mention the dispatcher for the coroutine based on the work a function does, We don't need to do the same for the caller of the suspending function. i.e., The suspend function that does the actual work handles what dispatcher to use, and the caller of the function should not worry about it.&lt;/p&gt;

&lt;p&gt;In fact, various libraries explicitly provide suspending APIs for long-running blocking operations. The complexity of moving the appropriate tasks to background threads is already taken care of within the library. When calling the library's suspending API, it does not have to be considered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S:&lt;/strong&gt; &lt;code&gt;Dispatchers.Unconfined&lt;/code&gt; is not recommended to be used in production code and is not discussed here.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to do
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;When you write a long-running or CPU intensive task, wrap the code with the appropriate dispatcher so that the caller of the function need not worry about it.&lt;/li&gt;
&lt;li&gt;If the library provides suspending APIs, the library will take care of the dispatchers. (It's a good practice to check the documentation of the library to see if it provides suspending APIs)&lt;/li&gt;
&lt;li&gt;If you are calling an existing suspending function, check if it uses the appropriate dispatcher. If not, wrap it with appropriate dispatcher to avoid mentioning one in the caller of the function.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to do
&lt;/h2&gt;

&lt;p&gt;Here's an example:&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;fun&lt;/span&gt; &lt;span class="nf"&gt;onDoneClicked&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;initiateSeeding&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="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;initiateSeeding&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;async&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Do some work that runs in IO thread&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;ol&gt;
&lt;li&gt;
&lt;p&gt;Wrap the long-running IO task with &lt;code&gt;Dispatcher.IO&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;
&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;initiateSeeding&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;async&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;Dispatcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;IO&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Do some work that runs in IO thread&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;/li&gt;
&lt;li&gt;
&lt;p&gt;Remove the dispatcher from the caller of the function&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;onDoneClicked&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;initiateSeeding&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;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Another good example is Retrofit. Retrofit provides suspending APIs for network calls. The library takes care of the dispatchers, and the caller of the function need not worry about it.&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="kd"&gt;interface&lt;/span&gt; &lt;span class="nc"&gt;SeedApiService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;@GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"seeds"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getSeeds&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Seed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SeedRepository&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;seedApiService&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SeedApiService&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;getSeeds&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="nc"&gt;List&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Seed&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;seedApiService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSeeds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SeedViewModel&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;seedRepository&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;SeedRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;fetchSeeds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;seeds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;seedRepository&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getSeeds&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="c1"&gt;// Do something with the seeds&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;e&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="c1"&gt;// Handle error&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The library will handle the dispatchers, and We don't need to mention it in the &lt;code&gt;viewModelScope.launch&lt;/code&gt; function.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://amitshekhar.me/blog/retrofit-with-kotlin-coroutines"&gt;Retrofit Suspended Functions&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.lukaslechner.com/do-i-need-to-call-suspend-functions-of-retrofit-and-room-on-a-background-thread/"&gt;Do-i-need-to-call-suspend-functions-of-retrofit-and-room-on-a-background-thread&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>android</category>
      <category>viewmodel</category>
      <category>codequality</category>
      <category>sonarlint</category>
    </item>
    <item>
      <title>This function has too many params</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Sat, 05 Aug 2023 22:49:30 +0000</pubDate>
      <link>https://forem.com/subbramanil/this-function-has-too-many-params-3388</link>
      <guid>https://forem.com/subbramanil/this-function-has-too-many-params-3388</guid>
      <description>&lt;h3&gt;
  
  
  Why
&lt;/h3&gt;

&lt;p&gt;As we add new functionality to an existing codebase, we tend to add more and more parameters to existing functions. Often, We want to minimize the changes to the current code and wrap the feature/bugfix quickly. While the intention is good, it can lead to many problems in the future. &lt;/p&gt;

&lt;p&gt;Depending on the project, this number can vary. As per sonarlint, this number should be less than 7. Anything more than that indicates that either, &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Function is doing too much&lt;/li&gt;
&lt;li&gt;Parameters can be grouped to form a new class&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While one can suppress this warning using &lt;code&gt;@Suppress("LongParameterList"),&lt;/code&gt; it's better to refactor the code to avoid this warning.&lt;/p&gt;

&lt;h3&gt;
  
  
  What to do
&lt;/h3&gt;

&lt;p&gt;Depending on the function, there are different ways to refactor the code to avoid this warning.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check the usage of the parameters inside the function and see if you can

&lt;ol&gt;
&lt;li&gt;Remove parameters that may serve the same purpose&lt;/li&gt;
&lt;li&gt;Split the function into multiple functions&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;li&gt;Group related parameters into a new class and pass the class instance as a parameter&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to do
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;showErrorDialog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;title&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;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;message&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;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;positiveTitle&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;=&lt;/span&gt; &lt;span class="k"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;negativeTitle&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;=&lt;/span&gt; &lt;span class="k"&gt;null&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;positiveButtonTextColor&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="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="n"&gt;isCancelable&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;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;isSingleButtonDialog&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;true&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;inCompose&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;false&lt;/span&gt;
&lt;span class="p"&gt;)&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;One way to fix this is to group the parameters into a new class and create a new overloaded function that takes the new class as a parameter.&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="kd"&gt;data class&lt;/span&gt; &lt;span class="nc"&gt;DialogParams&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;title&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;=&lt;/span&gt; &lt;span class="k"&gt;null&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;message&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;=&lt;/span&gt; &lt;span class="k"&gt;null&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;positiveTitle&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;=&lt;/span&gt; &lt;span class="k"&gt;null&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;negativeTitle&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;=&lt;/span&gt; &lt;span class="k"&gt;null&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;positiveButtonTextColor&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="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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;isCancelable&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;true&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;isSingleButtonDialog&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;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;showErrorDialog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;dialogParams&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;DialogParams&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;inCompose&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;false&lt;/span&gt;
&lt;span class="p"&gt;)&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;Replace the function call with the new function and pass the parameters as a new class instance.&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;fun&lt;/span&gt; &lt;span class="nf"&gt;showLoginFailed&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;showErrorDialog&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nc"&gt;DialogParams&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Login Failed"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Please try again"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;positiveTitle&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Retry"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;negativeTitle&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Cancel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;positiveButtonTextColor&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;green&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;isCancelable&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="n"&gt;isSingleButtonDialog&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;true&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;You can remove the old function once you replace all the usages with the new function.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: The code examples are simplified for brevity. Ideally, use string resources instead of hardcoded strings.&lt;/p&gt;




</description>
      <category>android</category>
      <category>functions</category>
      <category>codequality</category>
      <category>sonarlint</category>
    </item>
    <item>
      <title>ViewModels should not expose suspending functions</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Mon, 31 Jul 2023 03:45:36 +0000</pubDate>
      <link>https://forem.com/subbramanil/viewmodels-should-not-expose-suspending-functions-1akp</link>
      <guid>https://forem.com/subbramanil/viewmodels-should-not-expose-suspending-functions-1akp</guid>
      <description>&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;Sometimes the developer may add a &lt;code&gt;suspend&lt;/code&gt; keyword to a function that calls another suspending function as that's the first auto-suggestion/warning provided by the IDE. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XqIOnyww--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3valpyh9vlkp5ama2fwu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XqIOnyww--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3valpyh9vlkp5ama2fwu.png" alt="IDE warning" width="800" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;IDE makes it easy for the developer to auto-fix the warning by adding the &lt;code&gt;suspend&lt;/code&gt; keyword to the function.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Sx0vZyU7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7zsi6yscw4unrozh84sj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Sx0vZyU7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7zsi6yscw4unrozh84sj.png" alt="IDE auto-suggestion" width="714" height="128"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;But, this breaks the rule &lt;strong&gt;Classes extending "ViewModel" should not expose suspending functions&lt;/strong&gt;. &lt;/p&gt;

&lt;p&gt;Views should not be responsible for directly triggering coroutines. Hence, ViewModel classes should prefer creating coroutines instead of exposing suspending functions to perform some piece of business logic. This approach allows for easier testing of your application, as ViewModel classes can be unit tested, whereas views require instrumentation tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;p&gt;When you find a function in a ViewModel which breaks this rule, follow these steps to refactor it:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find the need for the &lt;code&gt;suspend&lt;/code&gt; keyword in the function, i.e., check if the function is calling any other suspending function

&lt;ul&gt;
&lt;li&gt;e.g., &lt;code&gt;listOfPlants.collectLatest&lt;/code&gt; is a suspending function&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;See if you can wrap the suspending function call in a coroutine &amp;amp; use the appropriate scope &amp;amp; dispatcher

&lt;ul&gt;
&lt;li&gt;e.g., &lt;code&gt;viewModelScope.launch&lt;/code&gt; can be used to wrap the suspending function call&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Remove the &lt;code&gt;suspend&lt;/code&gt; keyword from the function&lt;/li&gt;
&lt;li&gt;Remove creating coroutines from the caller of the function if it is not needed anymore&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  How to do
&lt;/h2&gt;

&lt;p&gt;The example,&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="c1"&gt;// In ViewModel&lt;/span&gt;
&lt;span class="k"&gt;suspend&lt;/span&gt; &lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;listenForUpdates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;listOfPlants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collectLatest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;plants&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;plants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&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="n"&gt;isFavorite&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;favorites&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="c1"&gt;// do something with favorites&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;// In Fragment&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;onViewCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onViewCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lifecycleScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launchWhenStarted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listenForUpdates&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;could be refactored 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="c1"&gt;// In ViewModel&lt;/span&gt;
&lt;span class="k"&gt;fun&lt;/span&gt; &lt;span class="nf"&gt;listenForUpdates&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;viewModelScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;listOfPlants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;collectLatest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;plants&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
            &lt;span class="n"&gt;plants&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&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="n"&gt;isFavorite&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;let&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;favorites&lt;/span&gt; &lt;span class="p"&gt;-&amp;gt;&lt;/span&gt;
                &lt;span class="c1"&gt;// do something with favorites&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;// In Fragment&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;onViewCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;View&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Bundle&lt;/span&gt;&lt;span class="p"&gt;?)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;onViewCreated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;savedInstanceState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;lifecycleScope&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launchWhenStarted&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="n"&gt;viewModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;listenForUpdates&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;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/kotlin/coroutines/coroutines-best-practices#viewmodel-coroutines"&gt;Coroutines: Best practices&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>android</category>
      <category>viewmodel</category>
      <category>codequality</category>
      <category>sonarlint</category>
    </item>
    <item>
      <title>Don't expose Mutable Flow Types</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Sun, 23 Jul 2023 22:36:23 +0000</pubDate>
      <link>https://forem.com/subbramanil/dont-expose-mutable-flow-types-53a1</link>
      <guid>https://forem.com/subbramanil/dont-expose-mutable-flow-types-53a1</guid>
      <description>&lt;p&gt;One of the recommendation in designing UI layer from the Android's guide to app architecture is to use '&lt;strong&gt;Uni-Directional Data Flow&lt;/strong&gt;'. This is achieved by viewModels exposing UI state via observer pattern and receive events from UI via callbacks. &lt;/p&gt;

&lt;p&gt;One of the best practice in exposing the UI state is to use immutable data types like &lt;code&gt;LiveData&lt;/code&gt; or &lt;code&gt;StateFlow&lt;/code&gt;. But, sometimes, we may be tempted to use &lt;code&gt;MutableStateFlow&lt;/code&gt; or &lt;code&gt;MutableSharedFlow&lt;/code&gt; to expose the UI state. While they are very convenient for storing and adding updates of some data structures in event-driven paradigm &amp;amp; useful to manage such objects inside some class, it’s not recommended to expose them outside of the class.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;When properties of the types &lt;code&gt;MutableStateFlow&lt;/code&gt; or &lt;code&gt;MutableSharedFlow&lt;/code&gt; are accessible from outside of a class, data updates cannot be verified properly anymore.&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="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;isBiometricEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the above example, the value of &lt;code&gt;isBiometricEnabled&lt;/code&gt; can be changed from outside of the class. It is generally recommended to have only one class responsible for updating these flows, otherwise inconsistency issues and problems with maintainability, as well as bugs may be introduced.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;p&gt;To restrict write access, &lt;code&gt;StateFlow&lt;/code&gt; or &lt;code&gt;SharedFlow&lt;/code&gt; should be used together with &lt;code&gt;private MutableStateFlow&lt;/code&gt; or &lt;code&gt;MutableSharedFlow&lt;/code&gt; fields.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the variable is used only for reading, change its type to &lt;code&gt;StateFlow&lt;/code&gt; or &lt;code&gt;SharedFlow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;If the variable is used for writing, change its type to &lt;code&gt;private MutableStateFlow&lt;/code&gt; or &lt;code&gt;private MutableSharedFlow&lt;/code&gt; and add a &lt;code&gt;public&lt;/code&gt; property with &lt;code&gt;StateFlow&lt;/code&gt; or &lt;code&gt;SharedFlow&lt;/code&gt; type that exposes the value of the &lt;code&gt;private&lt;/code&gt; field&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  How to do
&lt;/h2&gt;

&lt;p&gt;Here's one way of refactoring the code.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Find the usages of the variable in question (i.e., one that has &lt;code&gt;MutableStateFlow&lt;/code&gt; or &lt;code&gt;MutableSharedFlow&lt;/code&gt; type)&lt;/li&gt;
&lt;li&gt;If the variable is used only for reading, change its type to &lt;code&gt;StateFlow&lt;/code&gt; or &lt;code&gt;SharedFlow&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If the variable is used for reading &amp;amp; writing,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new variable with &lt;code&gt;StateFlow&lt;/code&gt; or &lt;code&gt;SharedFlow&lt;/code&gt; type that reads from the &lt;code&gt;MutableStateFlow&lt;/code&gt; or &lt;code&gt;MutableSharedFlow&lt;/code&gt; variable (use some naming convention to indicate that naming is temporary)
&lt;/li&gt;
&lt;/ul&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;isBiometricEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&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;tempIsBiometricEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isBiometricEnabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Replace 'read' usages with the new variable&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Change the type of the &lt;code&gt;MutableStateFlow&lt;/code&gt; or &lt;code&gt;MutableSharedFlow&lt;/code&gt; variable to &lt;code&gt;private&lt;/code&gt; &amp;amp; rename it to indicate that it is &lt;code&gt;private&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_isBiometricEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&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;tempIsBiometricEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_isBiometricEnabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&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;Fix the naming of the new variable to match the naming convention&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight kotlin"&gt;&lt;code&gt;&lt;span class="k"&gt;private&lt;/span&gt; &lt;span class="kd"&gt;val&lt;/span&gt; &lt;span class="py"&gt;_isBiometricEnabled&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;MutableStateFlow&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&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;isBiometricEnabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="nc"&gt;StateFlow&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;_isBiometricEnabled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;asStateFlow&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;Check the usages to verify that &lt;code&gt;isBiometricEnabled&lt;/code&gt; is not used for writing anywhere outside of the class and &lt;code&gt;_isBiometricEnabled&lt;/code&gt; is not exposed outside of the class&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Here are some references to the articles that I found useful&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/kotlin/coroutines/coroutines-best-practices#mutable-types"&gt;coroutines-best-practices-mutable-types&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>android</category>
      <category>build</category>
      <category>gradle</category>
      <category>performance</category>
    </item>
    <item>
      <title>Non-Transitive R Classes</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Mon, 17 Jul 2023 06:47:26 +0000</pubDate>
      <link>https://forem.com/subbramanil/non-transitive-r-classes-4jp6</link>
      <guid>https://forem.com/subbramanil/non-transitive-r-classes-4jp6</guid>
      <description>&lt;p&gt;While upgrading my android project to use AGP 8.0.0, I noticed that android build analyzer had a recommendation to use non-transitive R classes to improve build speed. &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%2Fuploads%2Farticles%2F2ubmtjopkch8mno0wlzd.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%2Fuploads%2Farticles%2F2ubmtjopkch8mno0wlzd.png" alt="Build Analyzer Recommendation" width="800" height="91"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I was curious to know what it is and how it works. So, I did some research and here's what I found.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why
&lt;/h2&gt;

&lt;p&gt;Non-transitive R classes have been around for a while, but it was not enabled by default. With AGP 8.0.0, it is enabled by default. For the curious minds, I have included a blog that talks about the history of R classes and non-transitive R classes at the end of this article.&lt;/p&gt;

&lt;p&gt;Non-transitive R classes enable namespacing of each library’s R class so that its R class includes only the resources declared in the library itself and none from the library’s dependencies, thereby reducing the size of the R class for that library.&lt;/p&gt;

&lt;p&gt;For example, if the code uses a default animation &lt;code&gt;R.anim.nav_default_enter_anim&lt;/code&gt; which comes from the &lt;code&gt;androidx.navigation:navigation-ui-ktx&lt;/code&gt; dependency. Without using non-transitive R classes, The 'R' class of module has references to its own resources and transitive references to resources of navigation UI library.&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;import&lt;/span&gt; &lt;span class="nn"&gt;com.learning.compose.R&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;span class="nf"&gt;setStartAnimations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nav_default_enter_anim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nav_default_exit_anim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With non-transitive R classes enabled, this will force us to use the fully qualified R class name to access the resources in the dependency module, thus reducing the references in the 'R' class of 'compose' module. And also by referencing using fully qualified R class name, the Compose module doesn't have to be recompiled and the build speed is improved.&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="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStartAnimations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;androidx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nav_default_enter_anim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;androidx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nav_default_exit_anim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since the dependencies become more explicit with non-transitive R classes, the modularity of the code increases. It also decreases  complexity, since resources cannot come from transitive dependencies. It also increases the build speed(both &lt;em&gt;clean&lt;/em&gt; and &lt;em&gt;incremental&lt;/em&gt;) as promised by &lt;a href="https://youtu.be/S_zZRBJxTQ0?t=69" rel="noopener noreferrer"&gt;Android Developers from Android Dev Summit '21&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to do
&lt;/h2&gt;

&lt;p&gt;If you use a resource from another dependency/module,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your android project is 100% Kotlin Only, You are in luck. You can &lt;code&gt;alias&lt;/code&gt; imports of R classes of dependency modules to access resources in that module.&lt;/li&gt;
&lt;li&gt;If your project has some Java code references, You have to use fully qualified R class name to access resources in the dependency module&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Also, If you use a resource from another module, but you do not declare a dependency on that module, and you do not want to declare a dependency, you could simply duplicate resources in the app module&lt;/p&gt;

&lt;h2&gt;
  
  
  How to do
&lt;/h2&gt;

&lt;p&gt;Use Android Studio's &lt;code&gt;Refactor -&amp;gt; Migrate to non-transitive R classes&lt;/code&gt; to migrate the project to use non-transitive R classes&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;This will, &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Adds the below in gradle.properties
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="py"&gt;android.nonTransitiveRClass&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;ul&gt;
&lt;li&gt;Updates fully qualified R class name wherever it finds the non-transitive R class usage
&lt;/li&gt;
&lt;/ul&gt;

&lt;pre class="highlight kotlin"&gt;&lt;code&gt;  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setStartAnimations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;androidx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nav_default_enter_anim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;androidx&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;navigation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ui&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;R&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nav_default_exit_anim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;




&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;Additionally, If the code base is in Kotlin, You could &lt;code&gt;alias&lt;/code&gt; the fully qualified package name of the R class to access the resources&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;import&lt;/span&gt; &lt;span class="nn"&gt;androidx.navigation.ui.R&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nc"&gt;NavUiR&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;span class="nf"&gt;setStartAnimations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;NavUiR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nav_default_enter_anim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;NavUiR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nav_default_exit_anim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setExitAnimations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;activity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;NavUiR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nav_default_pop_enter_anim&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;NavUiR&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;anim&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nav_default_pop_exit_anim&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here are some references to the articles that I found useful&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://developer.android.com/build/optimize-your-build#use-non-transitive-r-classes" rel="noopener noreferrer"&gt;Improve Build speed Recommendation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.mobileit.cz/Blog/Pages/r-class.aspx" rel="noopener noreferrer"&gt;History of R classes &amp;amp; non-transitive R classes&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>android</category>
      <category>build</category>
      <category>gradle</category>
      <category>performance</category>
    </item>
    <item>
      <title>Slimming Down Repo-1</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Sun, 01 Jan 2023 22:23:00 +0000</pubDate>
      <link>https://forem.com/subbramanil/slimming-down-repo-1-3kcd</link>
      <guid>https://forem.com/subbramanil/slimming-down-repo-1-3kcd</guid>
      <description>&lt;p&gt;Most of the repository providers (Github, Bitbucket) have a size limitation on the repository size and mechanisms to store large files using LFS options. In most cases, a repository may not reach these typical max size limits enforced. However, it can happen over a long period unless certain measures are taken to reduce the file size.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Disclaimer:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;There are a lot of articles on what to commit to a repository and what to ignore. The assumption is that you have followed the guidelines not to store any runtime/build/temporary files, yet reach the size limit.&lt;/li&gt;
&lt;li&gt;There are a few commands that can permanently delete the data. The suggested approach to a clean-up is to create a new clone of the repo, execute the clean-up procedure and verify the results. If you are happy with what you have arrived at, then push the changes to your remote.&lt;/li&gt;
&lt;li&gt;Since the process involves re-writing history, you will need to 'force' push the changes to remote. So make sure to perform these commands with the approval of the team and only when it's required (i.e., The Git repo storage limit is reached).&lt;/li&gt;
&lt;/ol&gt;
&lt;/blockquote&gt;

&lt;p&gt;These are a few commands to identify the biggest files that could be potential files to remove. You can remove these files and commit, but this will not remove the pack files associated with the original commits.&lt;/p&gt;

&lt;p&gt;One way to remove the large files and the pack files associated with them is by using the &lt;code&gt;git filter-branch&lt;/code&gt; along with the &lt;code&gt;git reflog &amp;amp; git gc&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Before beginning, Here are a few commands that we use to identify and remove bigger files.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;To identify the size of the repo
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-scH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To identify the count of files &amp;amp; other info
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; git count-objects &lt;span class="nt"&gt;-vH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Identify the top 10 big files
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-ld&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="k"&gt;**&lt;/span&gt;/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;DOL[1,10]&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Identify the commits with the largest 'n' blobs (Sort ascending and list the last 'n' items)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  git verify-pack &lt;span class="nt"&gt;-v&lt;/span&gt; .git/objects/pack/&amp;lt;pack-name&amp;gt;.idx | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; 3 &lt;span class="nt"&gt;-n&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;List the files in a commit
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  git rev-list &lt;span class="nt"&gt;--objects&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &amp;lt;commit-id&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Remove the file and re-write the history
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  git filter-branch &lt;span class="nt"&gt;--index-filter&lt;/span&gt; &lt;span class="s1"&gt;'git rm --cached --ignore-unmatch &amp;lt;File-to-be-removed&amp;gt;'&lt;/span&gt; &lt;span class="nt"&gt;--tag-name-filter&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To prune older reflog entries
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  git reflog expire &lt;span class="nt"&gt;--expire&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--all&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;To perform clean up unnecessary files and optimize the local repository
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;  git gc &lt;span class="nt"&gt;--prune&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here's a sequence of actions I performed in one of my repositories to reduce the storage. (I didn't reach the storage limit, I performed these steps to demo the idea)&lt;/p&gt;

&lt;h3&gt;
  
  
  Identify
&lt;/h3&gt;

&lt;p&gt;Identify the biggest file in the repo&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="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-scH&lt;/span&gt;                                                                                                                                      
257M    &lt;span class="nb"&gt;.&lt;/span&gt;
257M    total

▶ git verify-pack &lt;span class="nt"&gt;-v&lt;/span&gt; .git/objects/pack/pack-3c30d356e18bda774eb13dc9e53929012ec06800.idx | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; 3 &lt;span class="nt"&gt;-n&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 2
f5ed007fc5ee61733ee9bec25fdeac3f0119644f blob   12362185 12166055 61415659
de3e5b333ba453655951cabdae20588419ef7fe0 blob   18025235 18030628 35112687

▶ git rev-list &lt;span class="nt"&gt;--objects&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt; | &lt;span class="nb"&gt;grep &lt;/span&gt;f5ed007fc5ee61733ee9bec25fdeac3f0119644f
f5ed007fc5ee61733ee9bec25fdeac3f0119644f Side_Projects/MemoryTiles/google-play-screenshots-v1-todoriliev.com.sketch/Data
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Removal
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;▶ git filter-branch &lt;span class="nt"&gt;--index-filter&lt;/span&gt; &lt;span class="s1"&gt;'git rm --cached --ignore-unmatch Side_Projects/MemoryTiles/google-play-screenshots-v1-todoriliev.com.sketch'&lt;/span&gt; &lt;span class="nt"&gt;--tag-name-filter&lt;/span&gt; &lt;span class="nb"&gt;cat&lt;/span&gt; &lt;span class="nt"&gt;--&lt;/span&gt; &lt;span class="nt"&gt;--all&lt;/span&gt;
WARNING: git-filter-branch has a glut of gotchas generating mangled &lt;span class="nb"&gt;history
         &lt;/span&gt;rewrites.  Hit Ctrl-C before proceeding to abort, &lt;span class="k"&gt;then &lt;/span&gt;use an
         alternative filtering tool such as &lt;span class="s1"&gt;'git filter-repo'&lt;/span&gt;
         &lt;span class="o"&gt;(&lt;/span&gt;https://github.com/newren/git-filter-repo/&lt;span class="o"&gt;)&lt;/span&gt; instead.  See the
         filter-branch manual page &lt;span class="k"&gt;for &lt;/span&gt;more details&lt;span class="p"&gt;;&lt;/span&gt; to squelch this warning,
         &lt;span class="nb"&gt;set &lt;/span&gt;&lt;span class="nv"&gt;FILTER_BRANCH_SQUELCH_WARNING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1.
Proceeding with filter-branch...
...
...
Ref &lt;span class="s1"&gt;'refs/heads/main'&lt;/span&gt; was rewritten
Ref &lt;span class="s1"&gt;'refs/remotes/origin/ESI_Archive'&lt;/span&gt; was rewritten
Ref &lt;span class="s1"&gt;'refs/remotes/origin/main'&lt;/span&gt; was rewritten
WARNING: Ref &lt;span class="s1"&gt;'refs/remotes/origin/main'&lt;/span&gt; is unchanged
Ref &lt;span class="s1"&gt;'refs/stash'&lt;/span&gt; was rewritten

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Clean-up
&lt;/h3&gt;

&lt;p&gt;The git filter-branch command will create backup refs in &lt;code&gt;.git/refs/original&lt;/code&gt;. These refs must be deleted in order to remove references to these objects. Also, it's good to perform a garbage collection to do some clean-up.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;▶ git &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="nt"&gt;-each-ref&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"%(refname)"&lt;/span&gt; refs/original/ | &lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;read &lt;/span&gt;ref&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;git update-ref &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="nv"&gt;$ref&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt;  

▶ git reflog expire &lt;span class="nt"&gt;--expire&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now &lt;span class="nt"&gt;--all&lt;/span&gt;

▶ git gc &lt;span class="nt"&gt;--prune&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;now
Enumerating objects: 7802, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Counting objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;7802/7802&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Delta compression using up to 10 threads
Compressing objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;3950/3950&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Writing objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;7802/7802&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Total 7802 &lt;span class="o"&gt;(&lt;/span&gt;delta 4367&lt;span class="o"&gt;)&lt;/span&gt;, reused 6593 &lt;span class="o"&gt;(&lt;/span&gt;delta 3732&lt;span class="o"&gt;)&lt;/span&gt;, pack-reused 0

▶ git verify-pack &lt;span class="nt"&gt;-v&lt;/span&gt; .git/objects/pack/pack-358d01cc2715da3d0f49ccea3e5d3352e596e7c0.idx | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-k&lt;/span&gt; 3 &lt;span class="nt"&gt;-n&lt;/span&gt; | &lt;span class="nb"&gt;tail&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 2 
20ca85599c3decf2a972b0ede24ac0a8231b4cd9 blob   7218535 239853 111309837
b9785b3f3b5cddb633e7b2204d08c4bfd32ca501 blob   7608065 7502636 55950582

▶ git push
To github.com:subbramanil/my-dev-notes.git
 &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt;rejected]        main -&amp;gt; main &lt;span class="o"&gt;(&lt;/span&gt;fetch first&lt;span class="o"&gt;)&lt;/span&gt;
error: failed to push some refs to &lt;span class="s1"&gt;'github.com:subbramanil/my-dev-notes.git'&lt;/span&gt;
hint: Updates were rejected because the remote contains work that you &lt;span class="k"&gt;do
&lt;/span&gt;hint: not have locally. This is usually caused by another repository pushing
hint: to the same ref. You may want to first integrate the remote changes
hint: &lt;span class="o"&gt;(&lt;/span&gt;e.g., &lt;span class="s1"&gt;'git pull ...'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; before pushing again.
hint: See the &lt;span class="s1"&gt;'Note about fast-forwards'&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="s1"&gt;'git push --help'&lt;/span&gt; &lt;span class="k"&gt;for &lt;/span&gt;details.

Documents/personal/my-dev-notes  main ✔                                                                                                                                          2h3m  ⍉
▶ git push &lt;span class="nt"&gt;-f&lt;/span&gt;
Enumerating objects: 7796, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Counting objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;7796/7796&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Delta compression using up to 10 threads
Compressing objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;3312/3312&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Writing objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;7796/7796&lt;span class="o"&gt;)&lt;/span&gt;, 133.17 MiB | 2.66 MiB/s, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Total 7796 &lt;span class="o"&gt;(&lt;/span&gt;delta 4364&lt;span class="o"&gt;)&lt;/span&gt;, reused 7795 &lt;span class="o"&gt;(&lt;/span&gt;delta 4364&lt;span class="o"&gt;)&lt;/span&gt;, pack-reused 0
remote: Resolving deltas: 100% &lt;span class="o"&gt;(&lt;/span&gt;4364/4364&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
To github.com:subbramanil/my-dev-notes.git
 + 734e92e...71bd9bb main -&amp;gt; main &lt;span class="o"&gt;(&lt;/span&gt;forced update&lt;span class="o"&gt;)&lt;/span&gt;

▶ &lt;span class="nb"&gt;du&lt;/span&gt; &lt;span class="nt"&gt;-scH&lt;/span&gt;
212M    &lt;span class="nb"&gt;.&lt;/span&gt;
212M    total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reducing a repo size from 257 MB to 212 MB (45 MB) may not look like a big saving, however, the approach can be applied repeatedly to remove the bigger files to reduce the size of the repo.&lt;/p&gt;




&lt;p&gt;I found out there there are two alternatives to achieve similar results. I will write a follow-up blog on these two.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/newren/git-filter-repo/"&gt;git filter-repo&lt;/a&gt; (As noticed in the command line logs of &lt;code&gt;git filter-branch&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href="https://rtyley.github.io/bfg-repo-cleaner/"&gt;BFG&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




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

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://support.acquia.com/hc/en-us/articles/360004334093-Removing-large-files-from-Git-without-losing-history"&gt;Post-1&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://link-intersystems.com/blog/2014/07/17/remove-directories-and-files-permanently-from-git/"&gt;Post-2&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://stackoverflow.com/questions/11050265/remove-large-pack-file-created-by-git"&gt;StackOverFlow Post&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>git</category>
      <category>productivity</category>
      <category>commandline</category>
    </item>
    <item>
      <title>Extending beyond contributing to Github repos</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Mon, 16 Mar 2020 21:32:15 +0000</pubDate>
      <link>https://forem.com/subbramanil/extending-beyond-contributing-to-github-repos-483b</link>
      <guid>https://forem.com/subbramanil/extending-beyond-contributing-to-github-repos-483b</guid>
      <description>&lt;p&gt;&lt;em&gt;Hactoberfest&lt;/em&gt; is what triggered me to start contributing to the open-source rather than being a bystander on the open-source community. Whenever I get some time, I am contributing to open source repositories since I first participated in Hacktoberfest. The goal was to exercise what I know already and learn new things from the open-source repositories.&lt;/p&gt;

&lt;p&gt;While working on the open-source repositories, I always try to stick to the contributor guidelines and be conscious of the changes I propose. In the absence of contributor guidelines, I tried to make judgments based on common sense and proposed changes. Most of the time, it worked, and there were few exceptions where the maintainer didn't see the same way as I did. We agreed to ignore the changes and closed the Pull request. Sometimes the repository isn't actively maintained, which results in the pull requests being ignored by the maintainer.&lt;/p&gt;

&lt;p&gt;I would like to know the opinions of the community on the below situations. What's the recommended way?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; For the discussion, please assume the repositories are MIT/apache licensed, and the intention is not getting around licensing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario #1:&lt;/strong&gt; There's an open-sourced android app 'A.'&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is released in the play store&lt;/li&gt;
&lt;li&gt;Has less than 50k installs&lt;/li&gt;
&lt;li&gt;Actively maintained by the developer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You propose a feature/behavior change that adds value to the app but doesn't directly fall in the line of the maintainer's vision. The new feature/behavior change has potential &amp;amp; can still be extended.&lt;/p&gt;

&lt;p&gt;Assuming you feel that you would like to continue in your direction, Would you create a clone of the app &amp;amp; release it under a different name?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario #2:&lt;/strong&gt; Same scenario as #1&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is released in the play store&lt;/li&gt;
&lt;li&gt;Has less than 50k installs&lt;/li&gt;
&lt;li&gt;Not actively maintained by the developer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You propose a feature/behavior change that adds value to the app but doesn't directly fall in the line of the maintainer's vision. The new feature/behavior change has potential &amp;amp; can still be extended.  &lt;/p&gt;

&lt;p&gt;Assuming you feel that you would like to continue in your direction, Would you create a clone of the app &amp;amp; release it under a different name?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Scenario #3:&lt;/strong&gt; There's an open-sourced android app 'B,'&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is released in the play store&lt;/li&gt;
&lt;li&gt;Has less than 50k installs&lt;/li&gt;
&lt;li&gt;Actively maintained by the developer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You like the architecture/design/UI of the app and feel it could be a great base code for a different app idea you have in mind.&lt;/p&gt;

&lt;p&gt;How would you go about creating your app? Create a new repo and import the code and modify it for your use?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>opensource</category>
      <category>github</category>
    </item>
    <item>
      <title>Mystery Sticker Pack 2019</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Fri, 17 Jan 2020 21:04:53 +0000</pubDate>
      <link>https://forem.com/subbramanil/mystery-sticker-pack-2019-19lk</link>
      <guid>https://forem.com/subbramanil/mystery-sticker-pack-2019-19lk</guid>
      <description>&lt;p&gt;Quick shout out to the fantastic Dev.to team!! &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; This post is a bit late, I received the package a couple of weeks ago.&lt;/p&gt;

&lt;p&gt;I like to collect stickers from only those technologies I have gained experience from &amp;amp; services/websites that are meaningful to my career. So happy to get the Special gift stickers for DEV open-source contributors. &lt;/p&gt;

&lt;p&gt;Thank you so much for the awesome Dev.to Team for the excellent platform for the developers &amp;amp; continue to keep engagement through the new features.&lt;/p&gt;

&lt;p&gt;P.S: The commit history is hilarious!! &lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--2NCLZyaX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0n2p1l1v24lw366ydz3n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--2NCLZyaX--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/0n2p1l1v24lw366ydz3n.png" alt="Commit History"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>stickers</category>
      <category>gratitude</category>
    </item>
    <item>
      <title>Show Love Through Tech</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Wed, 18 Dec 2019 21:57:54 +0000</pubDate>
      <link>https://forem.com/subbramanil/show-love-through-tech-1bik</link>
      <guid>https://forem.com/subbramanil/show-love-through-tech-1bik</guid>
      <description>&lt;p&gt;When you are a nerd, What do you do to show your love?&lt;/p&gt;

&lt;p&gt;As I was cleaning up my storage space in my apartment, I found my old cache of IoT related materials. Breadboards, Resistors, Capacitors, LEDs, Different types of sensors, Raspberry PIs &amp;amp; Arduino, and of course, lots of jump-wires. I used to play around with sensors, making a lot of fun with my roommates at college. Seeing that I had a few hours available over the weekend, I thought of setting up at least one sensor and test it out.&lt;/p&gt;

&lt;p&gt;I typically use Raspbian for the ease of installation &amp;amp; familiar Debian environment. I downloaded the image, flashed to SDCard. I had no extra monitor at home, so I planned to login via SSH to my Raspberry Pi. I came to It's been a while since I used Raspbian, I didn't realize Raspbian has updated their security &amp;amp; blocked ssh on boot. I found out the correct way to enable SSH on boot &amp;amp; logged in.&lt;/p&gt;

&lt;p&gt;I had a bunch of motion sensors, and I was quickly able to put it together and test it out with one of my old programs for motion testing. I showed it to my wife; Having a Bachelor of Engineering in an Electronics, the demo didn't impress her a lot. In any case, I was happy that I didn't lose touch.&lt;/p&gt;

&lt;p&gt;I wanted to do something a bit more fun, and I found an LED matrix in the cache, which I didn't remove from the package. (Guess, I planned to do something with it but closed the shop even before trying it out).&lt;/p&gt;

&lt;p&gt;I typically prefer &lt;em&gt;node.js&lt;/em&gt; for working on IoT projects. Looking for an npm package to work with LED matrix, I found several, but none seem promising (Most of them weren't up to date &amp;amp; I lost patience before getting them work). Then I came across the Python library &lt;strong&gt;luma-led-matrix&lt;/strong&gt;, which was pretty quick to set up &amp;amp; had a bunch of examples that work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Details of implementation
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Hardware Required
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Raspberry PI 2/3/4&lt;/li&gt;
&lt;li&gt;MAX7219 8*8 LED matrix&lt;/li&gt;
&lt;li&gt;Jumper wires&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Software Required
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Raspbian OS&lt;/li&gt;
&lt;li&gt;Luma LED Matrix library&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  GPIO pin-outs
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Board Pin&lt;/th&gt;
&lt;th&gt;Name&lt;/th&gt;
&lt;th&gt;Remarks&lt;/th&gt;
&lt;th&gt;Rpi pin&lt;/th&gt;
&lt;th&gt;Rpi function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;VCC&lt;/td&gt;
&lt;td&gt;+5V Power&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;5V0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;td&gt;Ground&lt;/td&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;GND&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;DIN&lt;/td&gt;
&lt;td&gt;Data In&lt;/td&gt;
&lt;td&gt;19&lt;/td&gt;
&lt;td&gt;GPIO 10 (MOSI)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;CS&lt;/td&gt;
&lt;td&gt;Chip Select&lt;/td&gt;
&lt;td&gt;24&lt;/td&gt;
&lt;td&gt;GPIO 8 (SPI CE0)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;CLK&lt;/td&gt;
&lt;td&gt;Clock&lt;/td&gt;
&lt;td&gt;23&lt;/td&gt;
&lt;td&gt;GPIO 11 (SPI CLK)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For more details:&lt;br&gt;
&lt;a href="https://luma-led-matrix.readthedocs.io/en/latest/"&gt;luma-led-matrix documentation&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I had to enable SPI, and there are enough tutorials on how to do it. Here's the one I followed: &lt;a href="https://learn.sparkfun.com/tutorials/raspberry-pi-spi-and-i2c-tutorial#spi-on-pi"&gt;Sparkfun Tutorial on Enabling SPI on RaspberryPI&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I showed my wife the demo of one of the examples, and she was a bit impressed.&lt;/p&gt;

&lt;p&gt;The following day was '&lt;em&gt;her&lt;/em&gt;' day (meaning she can sleep as much as she wants and get up when she wants). So before I get to work, I changed the program to print, "I love you Mahi" &amp;amp; put it in a loop and left.&lt;/p&gt;

&lt;p&gt;I received a text from her around lunchtime that she loved it. Finally &lt;strong&gt;Yeah!!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I'm sure that I'm not the only nerd who wanted to show off to loved ones.  What's your experience in doing so?&lt;/p&gt;

</description>
      <category>raspberrypi</category>
      <category>iot</category>
      <category>lcdmatrix</category>
    </item>
    <item>
      <title>Fastlane Setup in Ubuntu</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Fri, 13 Dec 2019 16:42:54 +0000</pubDate>
      <link>https://forem.com/subbramanil/fastlane-setup-in-ubuntu-2dfb</link>
      <guid>https://forem.com/subbramanil/fastlane-setup-in-ubuntu-2dfb</guid>
      <description>&lt;p&gt;The recipe we follow at my work for automating android app publishing is bitbucket + Teamcity + Fastlane + slack.&lt;/p&gt;

&lt;p&gt;Choosing Fastlane for automatic publishing mobile apps to the store is no wonder. It's the most preferred way to publish apps automatically.&lt;/p&gt;

&lt;p&gt;We use TeamCity for CICD at our work, We looked at a few of the cloud solutions for CICD, but finally, we opted to use TeamCity Professional Version as our CICD tool. It's free to use and 3 build agents with 100 build configurations which were more than enough for our purposes. And comparatively, We felt it was way easier to set it up and configure than Jenkins.&lt;/p&gt;

&lt;p&gt;Of course, Jenkins has been there for a while and may have more community support and plugins than TeamCity, TeamCity felt easy to manage without a dedicated person for support. We did play around several other CICDs but finally settled with TeamCity.&lt;/p&gt;

&lt;p&gt;Fastlane is officially supported to run on macOS, and it seems like Windows &amp;amp; Linux Support is coming up.&lt;/p&gt;

&lt;p&gt;Since our TeamCity server (&amp;amp; the in-built build agent) was running on Ubuntu 14.04, We needed to install Fastlane in the same machine. The installation instructions for Fastlane on Ubuntu 18.04 is easy to find in &lt;a href="https://github.com/fastlane/fastlane/issues/11687#issuecomment-489891745"&gt;comments of an issue&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Here are the installation steps for Ubuntu 18.04 from the comments.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Install ruby and ruby-dev headers&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;ruby ruby-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Set environment language variables. Add these lines to the top of ~/.profile&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LC_ALL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en_US.UTF-8
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;LANG&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;en_US.UTF-8
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check that you didn't break the environment and restart. Run&lt;br&gt;
&lt;br&gt;
&lt;code&gt;source ~/.profile&lt;/code&gt;&lt;br&gt;
&lt;br&gt;
in the terminal. If no output, you are safe. Otherwise, check for what you broke in the .profile file&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Install Fastlane&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;fastlane
which fastlane  // Verify
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;From there do the normal &lt;a href="https://docs.fastlane.tools/getting-started/android/setup/"&gt;Fastlane Setup&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I ran into some issues while doing this setup. Some of the Gotchas while doing this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Make sure to install Ruby 2.x version&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-add-repository ppa:brightbox/ruby-ng
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ruby2.4
ruby &lt;span class="nt"&gt;-v&lt;/span&gt; // Verify
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ruby2.4-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Depends on your Ubuntu 14.04 environment, Sometimes the following needs to be installed&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;libc6-dev
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;g++
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fastlane uses the Google API client to communicate with the Google Play Console Services. I noticed this issue, '&lt;em&gt;Missing Google API Client&lt;/em&gt;' when installing Fastlane.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;google-api-client &lt;span class="nt"&gt;-v&lt;/span&gt; 0.29.1
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That should be it, Hope that helps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;P.S:&lt;/strong&gt; I wrote the draft of this blog a very long time ago and left it to pickle :D. I just decided to publish it after correcting the grammatical and misspelled words. Some of the errors may have been fixed.&lt;/p&gt;

</description>
      <category>android</category>
      <category>fastlane</category>
      <category>ubuntu</category>
    </item>
    <item>
      <title>HacktobeferFest:2019 Completed</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Thu, 31 Oct 2019 16:19:38 +0000</pubDate>
      <link>https://forem.com/subbramanil/hacktobeferfest-2019-completed-lc1</link>
      <guid>https://forem.com/subbramanil/hacktobeferfest-2019-completed-lc1</guid>
      <description>&lt;p&gt;Yeah, I did it, 2nd time completing Hacktoberfest. It's been extra fun working on Hacktoberfest this year (extra special coz., I had loads of fun working on a puzzle game in android).&lt;/p&gt;

&lt;p&gt;First of all, Huge thanks to &lt;strong&gt;DigitalOcean&lt;/strong&gt; and &lt;strong&gt;dev.to&lt;/strong&gt; for organizing the Hacktoberfest 2019. Of course, the primary objective of my participation is to get the cool T-Shirts &amp;amp; Sticker and a self-gratifying of human behavior of achieving something. But along the way, I felt a bit happy about contributing to somebody's work and giving it back to the community.&lt;/p&gt;

&lt;p&gt;It all started with one of our dev.to community buddy &lt;br&gt;
Beautus S Gumede's question regarding how to write test cases a year back.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/sduduzog" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--NcVQkiGx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--6x6cJVjd--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/86317/e8658a7e-0e1b-4463-beff-49f974146b1d.jpg" alt="sduduzog image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/sduduzog/how-to-write-tests-for-an-android-app-3041" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;How to write tests for an android app?&lt;/h2&gt;
      &lt;h3&gt;Beautus S Gumede ・ Oct  5 '18 ・ 1 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#kotlin&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#java&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#android&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#discuss&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I was much more active in 'dev.to' than nowadays. What started as answering to a question, led me to complete Hacktoberfest in 2018.&lt;/p&gt;


&lt;div class="ltag__link"&gt;
  &lt;a href="/subbramanil" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__pic"&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--ocYVh8Da--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://res.cloudinary.com/practicaldev/image/fetch/s--Njfi1Tm6--/c_fill%2Cf_auto%2Cfl_progressive%2Ch_150%2Cq_auto%2Cw_150/https://dev-to-uploads.s3.amazonaws.com/uploads/user/profile_image/18587/ca1b8a36-25ff-4939-9a0f-0a43d8c586e4.JPG" alt="subbramanil image"&gt;
    &lt;/div&gt;
  &lt;/a&gt;
  &lt;a href="/subbramanil/hactoberfest-challenge-completed-e49" class="ltag__link__link"&gt;
    &lt;div class="ltag__link__content"&gt;
      &lt;h2&gt;HactoberFest Challenge: Completed&lt;/h2&gt;
      &lt;h3&gt;Subbu Lakshmanan ・ Oct 30 '18 ・ 3 min read&lt;/h3&gt;
      &lt;div class="ltag__link__taglist"&gt;
        &lt;span class="ltag__link__tag"&gt;#hacktoberfest&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#android&lt;/span&gt;
        &lt;span class="ltag__link__tag"&gt;#opensource&lt;/span&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/a&gt;
&lt;/div&gt;


&lt;p&gt;I started this year early. So the Hacktoberfest plan for this year is.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Plan&lt;/span&gt;
&lt;span class="p"&gt;
-&lt;/span&gt; [x] Identify repos to contribute (Criteria: Android, Java/Kotlin, Easy/Medium difficulty)
&lt;span class="p"&gt;-&lt;/span&gt; [x] Follow best coding practices &amp;amp; use conventional commits for git commits
&lt;span class="p"&gt;-&lt;/span&gt; [x] Submit PRs
&lt;span class="p"&gt;-&lt;/span&gt; [x] Get the HacktoberFest T-Shirt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  Learnings
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Lesson 1: You can learn a lot of new things when you review a repo &amp;amp; fix/add things
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Issue
&lt;/h4&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/vislyhq/rust-android-example/issues/1"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Build fails on Android Studio 3.4.x and 3.5, since libraries officially migrated to `androidx`
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#1&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/dpezely"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--yiqzTAoc--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/542883%3Fv%3D4" alt="dpezely avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/dpezely"&gt;dpezely&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/vislyhq/rust-android-example/issues/1"&gt;&lt;time&gt;Aug 23, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Thank you so much for publishing this wonderful guide with source code!&lt;/p&gt;
&lt;p&gt;Would you be so kind as to do a quick update, please?  There were significant changes in the Android development world since your post in January 2019.&lt;/p&gt;
&lt;p&gt;For instance, as of May 2019, Google recommends Kotlin for new development of Android apps-- you were one step ahead!  Migration to &lt;code&gt;androidx&lt;/code&gt; is out of preview and official, such that &lt;code&gt;android.support.v7.app&lt;/code&gt; becomes &lt;code&gt;androidx.appcompat.app&lt;/code&gt;, etc.  As noted in comments to the blog post, there are deprecation warnings when running the python-based utilities.&lt;/p&gt;
&lt;p&gt;Would you also note library versions used, please?&lt;/p&gt;
&lt;p&gt;The Rust parts seem fine as of &lt;code&gt;rustc&lt;/code&gt; v1.37.0, as expected.&lt;/p&gt;
&lt;p&gt;As I'm new to both Kotlin and Android development, it would be a while before I'd be able to submit a PR with any confidence for this issue-- sorry.&lt;/p&gt;
&lt;p&gt;Thanks!&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/vislyhq/rust-android-example/issues/1"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/theaiscope/GDD-app/issues/16"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Add lint and fix issues
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#16&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/eduardb"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s---ee_CojY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars3.githubusercontent.com/u/2426348%3Fv%3D4" alt="eduardb avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/eduardb"&gt;eduardb&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/theaiscope/GDD-app/issues/16"&gt;&lt;time&gt;Oct 09, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;What the title says.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/theaiscope/GDD-app/issues/16"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Several of my PRs focussed on improving the existing code by addressing lint warnings &amp;amp; suggestions based on my experience. Most of the lint warnings are pretty evident, and the android studio can help in resolving those. The below one was something I didn't come across and was glad to be bumped with the error. It allowed me to learn something new.&lt;/p&gt;

&lt;p&gt;While working on the 'MEME' app, I came across the below deprecation as a result of running 'lint.'&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Environment.getExternalStorageDirectory() is deprecated in SDK 29&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;p&gt;From a quick &lt;a href="https://commonsware.com/blog/2019/06/07/death-external-storage-end-saga.html"&gt;research&lt;/a&gt;, found out &lt;strong&gt;baseContext.getExternalFilesDir("directory_name_to_create")&lt;/strong&gt; can be used. And from the logs, able to understand the differences of their implementation as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;10-15 15:31:05.328 27301-27301/com.example.lenovo.meme D/Test: Directory Path: baseContext.getExternalFilesDir("Meme"): /storage/emulated/0/Android/data/com.example.lenovo.meme/files/Meme
10-15 15:31:05.333 27301-27301/com.example.lenovo.meme D/Test: Environment.getExternalStorageDirectory(): /storage/emulated/0
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;

&lt;h4&gt;
  
  
  Pull Requests
&lt;/h4&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/theaiscope/GDD-app/pull/20"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        hotfix: Addresses Lint Warnings
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#20&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/subbramanil"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ne4sUdnf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/9157911%3Fv%3D4" alt="subbramanil avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/subbramanil"&gt;subbramanil&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/theaiscope/GDD-app/pull/20"&gt;&lt;time&gt;Oct 23, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;ul&gt;
&lt;li&gt;Cleans up unused import statements&lt;/li&gt;
&lt;li&gt;Applies appropriate scope qualifiers (i.e., private) for variables &amp;amp; functions&lt;/li&gt;
&lt;li&gt;Moves hard-coded strings to String.xml &amp;amp; consts as required&lt;/li&gt;
&lt;li&gt;Cleans up Regular expressions by removing unnecessary character escapes&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Almost all of the fixes were applied automatically through Android Studio's Auto-Suggestions which made sense &amp;amp; appropriate. None of the changes should affect the functionality of the app. No files were removed, although there were unused files &amp;amp; references.&lt;/li&gt;
&lt;li&gt;Reports character escapes that are replaceable with the unescaped character without a change in meaning. Note that inside the square brackets of a character class, many escapes are unnecessary that would be necessary outside of a character class. For example the regex [.] is identical to [.]&lt;/li&gt;
&lt;li&gt;None of the changes should affect any functionality. But in case of any doubt, any change can be removed, analyzed &amp;amp; applied on another iteration.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Reference: #16&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/theaiscope/GDD-app/pull/20"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/vislyhq/rust-android-example/pull/2"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Migration to AndroidX
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#2&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/subbramanil"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ne4sUdnf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/9157911%3Fv%3D4" alt="subbramanil avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/subbramanil"&gt;subbramanil&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/vislyhq/rust-android-example/pull/2"&gt;&lt;time&gt;Oct 03, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;ul&gt;
&lt;li&gt;Addresses &lt;a href="https://github.com/vislyhq/rust-android-example/issues/1"&gt;https://github.com/vislyhq/rust-android-example/issues/1&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/vislyhq/rust-android-example/pull/2"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/harshsri2208/MEME/pull/11"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Migration to Latest Dev Tools
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#11&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/subbramanil"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ne4sUdnf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/9157911%3Fv%3D4" alt="subbramanil avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/subbramanil"&gt;subbramanil&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/harshsri2208/MEME/pull/11"&gt;&lt;time&gt;Oct 15, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;ul&gt;
&lt;li&gt;Migrates to 'kotlin' from 'java'&lt;/li&gt;
&lt;li&gt;Upgrades gradle &amp;amp; gradle wrapper&lt;/li&gt;
&lt;li&gt;Updates the compile &amp;amp; target SDK to fix warnings&lt;/li&gt;
&lt;li&gt;Moves from 'appcompat' to 'androidx' library usage&lt;/li&gt;
&lt;li&gt;Moves hard coded strings to 'Strings.xml'&lt;/li&gt;
&lt;li&gt;Removes unused resources&lt;/li&gt;
&lt;li&gt;Fixes Lint warnings&lt;/li&gt;
&lt;li&gt;Fixes typos&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This commit migrates the code base to the latest android development tools before adding new features. This will enable building new features quickly and attact more developers to work on.&lt;/p&gt;
&lt;p&gt;The migration is done as follows,&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Making use of Android Studio's tools: 'Convert Java to Kotlin' &amp;amp; 'Migrate to AndroidX'&lt;/li&gt;
&lt;li&gt;Analyze the project using lint and apply auto-suggesions wherever possible&lt;/li&gt;
&lt;li&gt;When auto-suggestion isn't available, apply sensible changes manually&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a href="https://ufile.io/e872fn1u" rel="nofollow"&gt;Preview: Gets deleted after 30 days from Oct 15&lt;/a&gt;&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/harshsri2208/MEME/pull/11"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h3&gt;
  
  
  Lesson 2: Be conscious of the boundaries when proposing the changes &amp;amp; ask first
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Issue
&lt;/h4&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/harshsri2208/MEME/issues/6"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        redesign/modify UI and UX
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#6&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/harshsri2208"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--lXwhR8K4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars2.githubusercontent.com/u/37096649%3Fv%3D4" alt="harshsri2208 avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/harshsri2208"&gt;harshsri2208&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/harshsri2208/MEME/issues/6"&gt;&lt;time&gt;Oct 10, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;Interested people can redesign UI/UX for a better user experience. The current UI has a single page for all the activities and looks space deficient. Providing different pages for all the activities would provide a better control over the app.&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/harshsri2208/MEME/issues/6"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Also, one thing I learned this year was to be conscious of the boundaries &amp;amp; propose the changes. Although the primary goal of an owner/maintainer of a repository to get their bugs fixed, the implementation we provide may not align with what they need/requirements some times.&lt;/p&gt;

&lt;p&gt;While working on this 'MEME' app, the question the maintainer was related to the UI/UX change. I had a pretty good idea for the UX flow, so I planned to work on it. However, I noticed that the app was written in Java, and as Google has been promoting Kotlin, I felt it would be better to migrate to 'Kotlin.' So I migrated the app from Java to Kotlin and worked on my idea and submitted PR.&lt;/p&gt;

&lt;p&gt;But as I was working, I realized that I didn't check with the maintainer if he wished to migrate to 'Kotlin.' I opened up a discussion with the maintainer, and as I expected, he didn't want to migrate to Kotlin. No hard feelings; of course, the maintainer of a repo retains the right to decide what's best.&lt;/p&gt;

&lt;p&gt;In any case, I submitted the PR as I wanted to increase the hacktoberfest count (to be honest). I added the idea &amp;amp; shared a video of the new UX in the comments, in case the maintainer wants to take a look.&lt;/p&gt;

&lt;p&gt;I wish I asked the maintainer before starting working on the issue if he wanted to migrate the app from Java to Kotlin. In any case, it was fun to work.&lt;/p&gt;

&lt;h3&gt;
  
  
  Lesson 3: Contributing is more fun when you work on a game
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Issue
&lt;/h4&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/nikhilbansal97/Memory-Game/issues/1"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Make UI more interactive
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#1&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/nikhilbansal97"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--_JzFseEe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars3.githubusercontent.com/u/18311406%3Fv%3D4" alt="nikhilbansal97 avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/nikhilbansal97"&gt;nikhilbansal97&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/nikhilbansal97/Memory-Game/issues/1"&gt;&lt;time&gt;Sep 30, 2018&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      
    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nikhilbansal97/Memory-Game/issues/1"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The most fun I had was working on this repo: Memory-Game, which was a single screen memory based puzzle game. The repository was already in 'kotlin,' and I got a couple of ideas of extending the concept. I proposed a simple idea of providing 'difficulty' and let the user choose and play. Another idea was to allow the user to start at the beginner level and increase the difficulty as he finishes each level. I submitted PRs for both, and It was enjoyable working on this app.&lt;/p&gt;

&lt;h4&gt;
  
  
  Pull Requests
&lt;/h4&gt;


&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/nikhilbansal97/Memory-Game/pull/12"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Migration to Latest Dev Tools
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#12&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/subbramanil"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ne4sUdnf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/9157911%3Fv%3D4" alt="subbramanil avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/subbramanil"&gt;subbramanil&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/nikhilbansal97/Memory-Game/pull/12"&gt;&lt;time&gt;Oct 16, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;feat: Migration to Latest Dev Tools&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- Upgrades gradle &amp;amp; gradle wrapper
- Updates the compile &amp;amp; target SDK to fix warnings
- Moves from 'appcompat' to 'androidx' library usage
- Moves hard coded strings to 'Strings.xml'
- Fixes Lint warnings

This commit migrates the code base to the latest android development tools before adding new features.

The migration is done as follows,

- Analyze the project using lint and apply auto-suggesions wherever possible
- When auto-suggestion isn't available, apply sensible changes manually
&lt;/code&gt;&lt;/pre&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nikhilbansal97/Memory-Game/pull/12"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/nikhilbansal97/Memory-Game/pull/13"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Simple Game Idea with three challenge levels
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#13&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/subbramanil"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ne4sUdnf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/9157911%3Fv%3D4" alt="subbramanil avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/subbramanil"&gt;subbramanil&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/nikhilbansal97/Memory-Game/pull/13"&gt;&lt;time&gt;Oct 16, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;pre&gt;&lt;code&gt;feat: Simple Game Idea with three challenge levels

- Adds new launcher activity with challenge levels
- Replaces the hard-coded number of tiles &amp;amp; tries with flexible number based on challenge

The idea is to present the user with three challenge levels

1. Easy
2. Medium
3. Hard

Based on the user choice, set the number of tiles &amp;amp; max. number of tries. The user can play as many times as he wants in the current challenge level. To choose a much challenging level, he has to go back to the start of the app and change and continue playing. We can track how quickly user clears a challenge and award points/stars.

For now,

Easy -&amp;gt; 4 tiles, 4 tries
Medium -&amp;gt; 6 tiles, 6 tries
Hard -&amp;gt; 8 tiles, 8 tries

The number of tiles &amp;amp; tries are chosen as the same for simplicity but can be improved.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Screenshots:&lt;/p&gt;
&lt;p&gt;Start&lt;/p&gt;
&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/9157911/66942220-358aad80-f00e-11e9-972b-c810e82b9daa.png" rel="nofollow"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--roLLB_0l--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/9157911/66942220-358aad80-f00e-11e9-972b-c810e82b9daa.png" alt="memorygame-GameActivity-challenges"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Easy&lt;/p&gt;
&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/9157911/66942239-3e7b7f00-f00e-11e9-8ac1-a8767359fdca.png" rel="nofollow"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--rAQGZRgx--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/9157911/66942239-3e7b7f00-f00e-11e9-8ac1-a8767359fdca.png" alt="memorygame-GameActivity-easy"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Medium&lt;/p&gt;
&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/9157911/66942255-489d7d80-f00e-11e9-89ba-5e877fca5397.png" rel="nofollow"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mCmYrbeg--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/9157911/66942255-489d7d80-f00e-11e9-89ba-5e877fca5397.png" alt="memorygame-GameActivity-medium"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Hard&lt;/p&gt;
&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/9157911/66942268-4dfac800-f00e-11e9-8e5f-6f4474b92733.png" rel="nofollow"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pEby3Saa--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/9157911/66942268-4dfac800-f00e-11e9-8e5f-6f4474b92733.png" alt="memorygame-GameActivity-hard"&gt;&lt;/a&gt;&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nikhilbansal97/Memory-Game/pull/13"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;
&lt;br&gt;
&lt;div class="ltag_github-liquid-tag"&gt;
  &lt;h1&gt;
    &lt;a href="https://github.com/nikhilbansal97/Memory-Game/pull/14"&gt;
      &lt;img class="github-logo" alt="GitHub logo" src="https://res.cloudinary.com/practicaldev/image/fetch/s--vJ70wriM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://practicaldev-herokuapp-com.freetls.fastly.net/assets/github-logo-ba8488d21cd8ee1fee097b8410db9deaa41d0ca30b004c0c63de0a479114156f.svg"&gt;
      &lt;span class="issue-title"&gt;
        Game with progressive levels
      &lt;/span&gt;
      &lt;span class="issue-number"&gt;#14&lt;/span&gt;
    &lt;/a&gt;
  &lt;/h1&gt;
  &lt;div class="github-thread"&gt;
    &lt;div class="timeline-comment-header"&gt;
      &lt;a href="https://github.com/subbramanil"&gt;
        &lt;img class="github-liquid-tag-img" src="https://res.cloudinary.com/practicaldev/image/fetch/s--ne4sUdnf--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://avatars0.githubusercontent.com/u/9157911%3Fv%3D4" alt="subbramanil avatar"&gt;
      &lt;/a&gt;
      &lt;div class="timeline-comment-header-text"&gt;
        &lt;strong&gt;
          &lt;a href="https://github.com/subbramanil"&gt;subbramanil&lt;/a&gt;
        &lt;/strong&gt; posted on &lt;a href="https://github.com/nikhilbansal97/Memory-Game/pull/14"&gt;&lt;time&gt;Oct 18, 2019&lt;/time&gt;&lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
    &lt;div class="ltag-github-body"&gt;
      &lt;p&gt;feat: Game with progressive levels&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- Adds 'level' to the game to track the current level
- Shows 'current level' before starting a game
- Makes dialog dismiss listener as optional to avoid the need of additional listener
- Removes code imported from simple game branch

The idea of the game is to start with level 1 and increase difficulty as you win.

Level 1..3 -&amp;gt; 4 tiles &amp;amp; 4 tries
Level 4..7 -&amp;gt; 8 tiles &amp;amp; 8 tries
Level 8..20 -&amp;gt; 10 tiles &amp;amp; 10 tries

Games starts with level 1 and as the player wins the game, he's taken to next level. Once the max level is reached, the game will end. As of the max level is set to 10.
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Level 1&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/9157911/67130871-5565d100-f1c8-11e9-8595-1de0fe28ea74.png" rel="nofollow"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--PVeuu9zw--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/9157911/67130871-5565d100-f1c8-11e9-8595-1de0fe28ea74.png" alt="memorygame-level-1"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Level 4&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/9157911/67130870-5565d100-f1c8-11e9-9f84-6062781ccff0.png" rel="nofollow"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Cxd__4X1--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/9157911/67130870-5565d100-f1c8-11e9-9f84-6062781ccff0.png" alt="memorygame-level-4"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Level 8&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/9157911/67130869-5565d100-f1c8-11e9-82e4-14c93ff0c2b9.png" rel="nofollow"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--5Z6xoP_N--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/9157911/67130869-5565d100-f1c8-11e9-82e4-14c93ff0c2b9.png" alt="memorygame-level-8"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
&lt;span class="octicon octicon-link"&gt;&lt;/span&gt;Game Completion&lt;/h2&gt;
&lt;p&gt;&lt;a href="https://user-images.githubusercontent.com/9157911/67130868-5565d100-f1c8-11e9-813d-9632e2b38204.png" rel="nofollow"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--IrVwsGd3--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://user-images.githubusercontent.com/9157911/67130868-5565d100-f1c8-11e9-813d-9632e2b38204.png" alt="memorygame-complete"&gt;&lt;/a&gt;&lt;/p&gt;

    &lt;/div&gt;
    &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/nikhilbansal97/Memory-Game/pull/14"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--O99H5G8G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5z9thiaimjwhb9lf9tzt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--O99H5G8G--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/5z9thiaimjwhb9lf9tzt.png" alt="Hacktober Completion Status"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>hacktoberfest</category>
      <category>android</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Learn by contributing to Open Source</title>
      <dc:creator>Subbu Lakshmanan</dc:creator>
      <pubDate>Mon, 17 Jun 2019 21:43:02 +0000</pubDate>
      <link>https://forem.com/subbramanil/learn-by-contributing-to-open-source-4kf</link>
      <guid>https://forem.com/subbramanil/learn-by-contributing-to-open-source-4kf</guid>
      <description>&lt;p&gt;In the recent Google I/O, Google announced &lt;strong&gt;Kotlin&lt;/strong&gt; to be the first choice language for writing Android apps. Since few years before Google announced that, &lt;strong&gt;Kotlin&lt;/strong&gt; is preferred language and added kotlin support to the android studio, this news doesn't come as a surprise for me.&lt;/p&gt;

&lt;p&gt;When the announcement came a year ago, I registered for many courses, finished a couple of them. I felt good about completing the exercises &amp;amp; sample projects. However, It lasted only for a while. With my personal (got married to a wonderful girl) &amp;amp; work commitments doubled(they let go of a few folks at my work), I took a break from learning kotlin.&lt;/p&gt;

&lt;p&gt;When I started again, I struggled a lot and confused as to where to start again, as I was in the middle of several kotlin courses. Although the courses I registered to learn kotlin were pretty good, I realized that I'm more of a reading person. I don't mind reading documentation or several blogs to understand a concept and try out on my own. Watching a video course and following along a tutorial felt too slow for me.&lt;/p&gt;

&lt;p&gt;I worked on small projects as I was learning. Although the personal projects were helpful to understand the fundamentals, it had few challenges.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Focus on scope:&lt;/strong&gt; It was up to me to decide the scope of the project. Sometimes I tend to expand on my idea on personal projects, and I spent more time on things that weren't relevant to kotlin.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Focus on Technology Stack:&lt;/strong&gt; Again, it's up to me to decide the architecture and design of the app, the libraries to use. While identifying answers to these technical questions, I lost track of my goal(which is learning Kotlin). For ex., I lost loads of time when I started with the goal of looking for blogs related with data classes and somehow followed blogs on sample apps that are built using the MVVM + RxKotlin + Dagger2. Although it is good to learn about this setup, it wasn't contributing directly to my original goal.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That's when I decided to start learning Kotlin again from scratch, but this time with a better clear plan.&lt;/p&gt;

&lt;p&gt;So the mistakes that I did earlier in learning Kotlin were,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Trying to learn Kotlin from too many sources&lt;/li&gt;
&lt;li&gt;Didn't start with what's working out well for myself (Reading vs. Video courses)&lt;/li&gt;
&lt;li&gt;Didn't have a plan with a defined schedule&lt;/li&gt;
&lt;li&gt;Didn't have a defined scope on personal projects&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I learned from my experience and created a plan.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal:&lt;/strong&gt; To Learn &amp;amp; develop an App in Kotlin and publish to the store by Sep 2019&lt;/p&gt;

&lt;p&gt;The plan to do that,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Buy a book for learning Kotlin: &lt;a href="https://www.amazon.com/Kotlin-Programming-Nerd-Ranch-Guide/dp/0135161630"&gt;Kotlin Programming: The Big Nerd Ranch Guide&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Work on Personal project with limited scope: An Android App

&lt;ol&gt;
&lt;li&gt;Outlined the basic idea of the app&lt;/li&gt;
&lt;li&gt;Identified the functionality scope of the app&lt;/li&gt;
&lt;li&gt;Identified the technology scope of the app&lt;/li&gt;
&lt;li&gt;Designed simple UI/UX flow using sketch app&lt;/li&gt;
&lt;/ol&gt;


&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Learn by Contributing to open source
&lt;/h2&gt;

&lt;p&gt;I have participated in Hactoberfest last year and contributed to &lt;a href="https://github.com/sduduzog/slim-launcher"&gt;Slim-Launcher&lt;/a&gt; app by &lt;a href="https://dev.to/sduduzog"&gt;Beautus S Gumede&lt;/a&gt;. I &lt;a href="https://dev.to/subbramanil/hactoberfest-challenge-completed-e49"&gt;completed my Hactoberfest challenge&lt;/a&gt; and got my T-Shirt as well. It felt good to give back to the community.&lt;/p&gt;

&lt;p&gt;When looking back,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I learned a lot while working on the project. The app uses Kotlin, and It was a good exercise for me to flex my skills.&lt;/li&gt;
&lt;li&gt;The app was published to store and had a defined technical scope to it. It helped me to focus on one thing and work within the boundary.&lt;/li&gt;
&lt;li&gt;I thoroughly enjoyed interacting with the project developer Beautus S Gumede and working on the project. While we both were strangers, connected through his blog on dev.to, I enjoyed working on his project and liked the candidness and the appreciative character of Beautus S Gumede.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This exercise made me realize that I could learn a lot more by working on projects with definitive design and functionality. I could look into the code, fix a bug, or add a feature and learn from the project.&lt;/p&gt;

&lt;p&gt;This had led me to add one more step to my learning plan,&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Buy a book for learning Kotlin&lt;/li&gt;
&lt;li&gt;Work on Personal project: Android App&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Contribute to open source projects&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So far, I have contributed to,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/sduduzog/slim-launcher"&gt;Slim-Launcher&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Fork of &lt;a href="https://github.com/TakWolf/Android-Lock9View"&gt;Android-Lock9View&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/subbramanil/DEV-Android"&gt;Dev-Android&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently, I'm working on an enhancement request in the projects,&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/subbramanil/SimpleToDo"&gt;Simple ToDo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/subbramanil/simple-android"&gt;Simple-Android&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I typically make notes of my learnings as I work on a project, and I plan to write a series of blogs once the PRs are submitted and merged.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; I found out about the repositories through Github "Discover Repositories" feature. However, now that we have a listing in 'Dev.to,' I'm going to create a listing for volunteering to work on open source Android/kotlin based projects where I can contribute and learn as well. (Although, I'm going to delay that until I finish my current commitments)&lt;/p&gt;

</description>
      <category>android</category>
      <category>kotlin</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
