<?xml version="1.0" encoding="UTF-8" standalone="no"?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" version="2.0"><channel><title>Androidians</title><description>Tutorials for Android - Share and Enjoy</description><managingEditor>noreply@blogger.com (androidians)</managingEditor><pubDate>Thu, 28 May 2026 17:37:10 +0530</pubDate><generator>Blogger http://www.blogger.com</generator><openSearch:totalResults xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">216</openSearch:totalResults><openSearch:startIndex xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">1</openSearch:startIndex><openSearch:itemsPerPage xmlns:openSearch="http://a9.com/-/spec/opensearchrss/1.0/">25</openSearch:itemsPerPage><link>http://smartandroidians.blogspot.com/</link><language>en-us</language><itunes:explicit>no</itunes:explicit><itunes:keywords>Android,Tutorials</itunes:keywords><itunes:subtitle>Tutorials for Android - Share and Enjoy</itunes:subtitle><itunes:category text="Technology"><itunes:category text="Software How-To"/></itunes:category><itunes:owner><itunes:email>noreply@blogger.com</itunes:email></itunes:owner><item><title>How to Set Up Multiple GitHub Accounts in Android Studio Using SSH</title><link>http://smartandroidians.blogspot.com/2026/05/how-to-set-up-multiple-github-accounts.html</link><category>android studio</category><category>bash</category><category>GitHub accounts</category><category>multiple github accounts</category><category>SSH keys</category><category>terminal</category><author>noreply@blogger.com (androidians)</author><pubDate>Thu, 28 May 2026 17:37:10 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-6996670758301105366</guid><description>&lt;p&gt;Managing multiple GitHub accounts on the same machine can be tricky, especially when switching between personal and work repositories. This guide explains how to configure multiple GitHub accounts using SSH in Android Studio and macOS/Linux terminals.&lt;/p&gt;&lt;hr /&gt;&lt;h1&gt;Step 1: Check Existing SSH Keys&lt;/h1&gt;&lt;p&gt;Open Terminal and run:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;ls -al ~/.ssh
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This shows any existing SSH keys already present on your system.&lt;/p&gt;&lt;hr /&gt;&lt;h1&gt;Step 2: Generate SSH Key for First GitHub Account&lt;/h1&gt;&lt;p&gt;Run:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;ssh-keygen -t ed25519 -C "first-account@example.com" -f ~/.ssh/id_first
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When prompted for a passphrase, you can press Enter twice to skip it.&lt;/p&gt;&lt;p&gt;Example output:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-text"&gt;Generating public/private ed25519 key pair.
Your identification has been saved in ~/.ssh/id_first
Your public key has been saved in ~/.ssh/id_first.pub
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Files Generated&lt;/h3&gt;&lt;table style="color: black; font-variant-caps: normal;"&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;File&lt;/th&gt;&lt;th&gt;Purpose&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code inline=""&gt;~/.ssh/id_first&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Private key (Never share)&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code inline=""&gt;~/.ssh/id_first.pub&lt;/code&gt;&lt;/td&gt;&lt;td&gt;Public key (Add to GitHub)&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;hr /&gt;&lt;h1&gt;Step 3: Generate SSH Key for Second GitHub Account&lt;/h1&gt;&lt;p&gt;Run:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;ssh-keygen -t ed25519 -C "second-account@example.com" -f ~/.ssh/id_second
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Example output:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-text"&gt;Your identification has been saved in ~/.ssh/id_second
Your public key has been saved in ~/.ssh/id_second.pub
&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;h1&gt;Step 4: Add Both Keys to SSH Agent&lt;/h1&gt;&lt;h2&gt;Start SSH Agent&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;eval "$(ssh-agent -s)"
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Add Both Keys&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;ssh-add ~/.ssh/id_first
ssh-add ~/.ssh/id_second
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Verify Loaded Keys&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;ssh-add -l
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;You should see both SSH keys listed.&lt;/p&gt;&lt;hr /&gt;&lt;h1&gt;Step 5: Configure SSH&lt;/h1&gt;&lt;p&gt;Open the SSH config file:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;nano ~/.ssh/config
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Add the following configuration:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-text"&gt;# First GitHub Account
Host github-first
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_first
  AddKeysToAgent yes

# Second GitHub Account
Host github-second
  HostName github.com
  User git
  IdentityFile ~/.ssh/id_second
  AddKeysToAgent yes
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Save and exit:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code inline=""&gt;Ctrl + O&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Press&amp;nbsp;&lt;code inline=""&gt;Enter&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code inline=""&gt;Ctrl + X&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr /&gt;&lt;h1&gt;Step 6: Add Public Keys to GitHub&lt;/h1&gt;&lt;h2&gt;First Account&lt;/h2&gt;&lt;p&gt;Display the public key:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;cat ~/.ssh/id_first.pub
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Copy the entire output.&lt;/p&gt;&lt;p&gt;In GitHub:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;Go to&amp;nbsp;&lt;strong&gt;Settings&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Open&amp;nbsp;&lt;strong&gt;SSH and GPG Keys&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Click&amp;nbsp;&lt;strong&gt;New SSH Key&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Paste the key&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Save&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;hr /&gt;&lt;h2&gt;Second Account&lt;/h2&gt;&lt;p&gt;Display the second public key:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;cat ~/.ssh/id_second.pub
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Repeat the same steps in the second GitHub account.&lt;/p&gt;&lt;hr /&gt;&lt;h1&gt;Step 7: Test SSH Connections&lt;/h1&gt;&lt;h2&gt;First Account&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;ssh -T git@github-first
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Expected output:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-text"&gt;Hi username! You've successfully authenticated, but GitHub does not provide shell access.
&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;h2&gt;Second Account&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;ssh -T git@github-second
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Expected output:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-text"&gt;Hi username! You've successfully authenticated, but GitHub does not provide shell access.
&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;h1&gt;Step 8: Clone Repositories Using Host Alias&lt;/h1&gt;&lt;h2&gt;Clone Repository for First Account&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;git clone git@github-first:organization/repository.git
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Clone Repository for Second Account&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;git clone git@github-second:username/repository.git
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Important&lt;/h3&gt;&lt;p&gt;Instead of using&amp;nbsp;&lt;code inline=""&gt;github.com&lt;/code&gt;, use the configured SSH aliases:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;code inline=""&gt;github-first&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;code inline=""&gt;github-second&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;hr /&gt;&lt;h1&gt;Step 9: Configure Git Identity Per Repository&lt;/h1&gt;&lt;p&gt;After cloning the repository:&lt;/p&gt;&lt;h2&gt;First Repository&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;cd first-repository/

git config user.name "Your Name"
git config user.email "first-account@example.com"
&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;h2&gt;Second Repository&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;cd second-repository/

git config user.name "Your Name"
git config user.email "second-account@example.com"
&lt;/code&gt;&lt;/pre&gt;&lt;h2&gt;Verify Configuration&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;git config --list | grep user
&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;h1&gt;Step 10: Update Existing Repositories&lt;/h1&gt;&lt;p&gt;If repositories were already cloned using&amp;nbsp;&lt;code inline=""&gt;github.com&lt;/code&gt;, update the remote URL.&lt;/p&gt;&lt;h2&gt;Check Current Remote&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;git remote -v
&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;h2&gt;Update Remote URL&lt;/h2&gt;&lt;h3&gt;First Account&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;git remote set-url origin git@github-first:organization/repository.git
&lt;/code&gt;&lt;/pre&gt;&lt;h3&gt;Second Account&lt;/h3&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;git remote set-url origin git@github-second:username/repository.git
&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;h2&gt;Verify Updated Remote&lt;/h2&gt;&lt;pre&gt;&lt;code class="language-bash"&gt;git remote -v
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Expected output:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-text"&gt;origin  git@github-first:organization/repository.git (fetch)
origin  git@github-first:organization/repository.git (push)
&lt;/code&gt;&lt;/pre&gt;&lt;hr /&gt;&lt;h1&gt;Common Error&lt;/h1&gt;&lt;p&gt;If you see:&lt;/p&gt;&lt;pre&gt;&lt;code class="language-text"&gt;fatal: No such remote 'origin'
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Make sure you are running the command from inside the correct Git repository folder.&lt;/p&gt;&lt;hr /&gt;&lt;h1&gt;Final Notes&lt;/h1&gt;&lt;p&gt;Using separate SSH keys and host aliases is the cleanest way to manage multiple GitHub accounts on the same machine. Once configured, Android Studio and Git automatically use the correct account for each repository.&lt;/p&gt;&lt;p&gt;This setup works well for:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;Personal + Work GitHub accounts&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Open-source contributions&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Freelance projects&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Multiple organizations&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Secure repository management&lt;/p&gt;&lt;p&gt;Note: We can use any name for alias, here I use &lt;b&gt;&lt;i&gt;github-first&lt;/i&gt;&lt;/b&gt; and &lt;b&gt;&lt;i&gt;github-second&lt;/i&gt;&lt;/b&gt;. We can also use, &lt;b&gt;&lt;i&gt;github-office&lt;/i&gt;&lt;/b&gt;, &lt;b&gt;&lt;i&gt;github-personal&lt;/i&gt;&lt;/b&gt; etc. It is upto us.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Happy Exploring!!!&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>WorkManager Policies Demystified: KEEP vs CANCEL_AND_REENQUEUE vs UPDATE (With Real Example)</title><link>http://smartandroidians.blogspot.com/2025/05/workmanager-policies-demystified-keep.html</link><category>Android</category><category>CANCEL_AND_REENQUEUE</category><category>ExistingPeriodicWorkPolicy</category><category>KEEP</category><category>perioidicWorker</category><category>REPLACE</category><category>Update</category><category>workManager</category><author>noreply@blogger.com (androidians)</author><pubDate>Thu, 8 May 2025 12:42:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-4542843667620758562</guid><description>&lt;p&gt;&amp;nbsp;When I first started using WorkManager in Android about five years ago, I had a lot of confusion—especially around configuring&amp;nbsp;&lt;strong data-end="271" data-start="251"&gt;periodic workers&lt;/strong&gt;. One of the biggest questions was:&lt;/p&gt;&lt;p data-end="376" data-start="124"&gt;&lt;strong data-end="376" data-start="309"&gt;Which&amp;nbsp;&lt;/strong&gt;ExistingPeriodicWorkPolicy&amp;nbsp;&lt;strong data-end="376" data-start="309"&gt;should I use—&lt;code data-end="343" data-start="337"&gt;KEEP&lt;/code&gt;,&amp;nbsp;&lt;code data-end="354" data-start="345"&gt;REPLACE&lt;/code&gt;, or something else?&lt;/strong&gt;&lt;/p&gt;&lt;p data-end="421" data-start="378"&gt;Let me break it down with a simple example:&lt;/p&gt;&lt;p data-end="605" data-start="423"&gt;Suppose you schedule a periodic worker to run every&amp;nbsp;&lt;strong data-end="489" data-start="475"&gt;30 minutes&lt;/strong&gt;. Later, you decide to change it to&amp;nbsp;&lt;strong data-end="542" data-start="525"&gt;every 4 hours&lt;/strong&gt;. Now, depending on the policy you choose, here’s what happens:&lt;/p&gt;&lt;ul data-end="1296" data-start="607"&gt;&lt;li data-end="749" data-start="607"&gt;&lt;p data-end="749" data-start="609"&gt;&lt;strong data-end="619" data-start="609"&gt;&lt;code data-end="617" data-start="611"&gt;KEEP&lt;/code&gt;&lt;/strong&gt;&amp;nbsp;– The existing worker will continue running every 30 minutes. Your new 4-hour interval is&amp;nbsp;&lt;strong data-end="721" data-start="710"&gt;ignored&lt;/strong&gt;. So the old schedule stays.&lt;/p&gt;&lt;/li&gt;&lt;li data-end="1008" data-start="753"&gt;&lt;p data-end="1008" data-start="755"&gt;&lt;strong data-end="768" data-start="755"&gt;&lt;code data-end="766" data-start="757"&gt;REPLACE&lt;/code&gt;&lt;/strong&gt;&amp;nbsp;&lt;em data-end="787" data-start="769"&gt;(now deprecated)&lt;/em&gt;&amp;nbsp;– The current worker is&amp;nbsp;&lt;strong data-end="825" data-start="812"&gt;cancelled&lt;/strong&gt;&amp;nbsp;and a new one is scheduled with the new interval. This is now replaced by a clearer version called&amp;nbsp;&lt;strong data-end="951" data-start="925"&gt;&lt;code data-end="949" data-start="927"&gt;CANCEL_AND_REENQUEUE&lt;/code&gt;&lt;/strong&gt;, which behaves the same but has a more descriptive name.&lt;/p&gt;&lt;/li&gt;&lt;li data-end="1296" data-start="1010"&gt;&lt;p data-end="1296" data-start="1012"&gt;&lt;strong data-end="1024" data-start="1012"&gt;&lt;code data-end="1022" data-start="1014"&gt;UPDATE&lt;/code&gt;&lt;/strong&gt;&amp;nbsp;– This is a newer option. If the worker is&amp;nbsp;&lt;strong data-end="1089" data-start="1068"&gt;currently running&lt;/strong&gt;, it will finish using the old configuration. But from the&amp;nbsp;&lt;strong data-end="1173" data-start="1148"&gt;next interval onwards&lt;/strong&gt;, it will use your&amp;nbsp;&lt;strong data-end="1212" data-start="1192"&gt;updated schedule&lt;/strong&gt;. So it doesn’t interrupt what’s already happening, just updates what's coming next.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p data-end="1454" data-start="1298"&gt;Knowing when and how these policies work can save you a lot of debugging time. For me, understanding this clearly made working with WorkManager much easier.&lt;/p&gt;&lt;p data-end="1454" data-start="1298"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p data-end="1454" data-start="1298"&gt;Happy exploring!!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Fetching Images from URLs in Jetpack Compose: Part 2 of the Login App</title><link>http://smartandroidians.blogspot.com/2024/09/fetching-images-from-urls-in-jetpack.html</link><category>Android</category><category>coil</category><category>compose</category><category>custom composable</category><category>declarative UI</category><category>decode</category><category>encode</category><category>Jetpack compose</category><category>NetworkImage</category><category>UTF_8</category><author>noreply@blogger.com (androidians)</author><pubDate>Sun, 29 Sep 2024 19:01:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-7881584228228900814</guid><description>&lt;p&gt;&amp;nbsp;In the first part of this series, I demonstrated how to populate images in our Jetpack Compose app using local resources from the&amp;nbsp;&lt;code&gt;drawable&lt;/code&gt;&amp;nbsp;folder. In this article, we’ll take a step further and fetch images from URLs instead. To achieve this, we’ll use the&amp;nbsp;&lt;strong&gt;Coil&lt;/strong&gt;&amp;nbsp;library, which simplifies image loading in Jetpack Compose.&lt;/p&gt;&lt;h4&gt;Adding Coil to the Project&lt;/h4&gt;&lt;p&gt;Since I’m using a Kotlin-based&amp;nbsp;&lt;code&gt;build.gradle&lt;/code&gt;&amp;nbsp;file, adding the Coil dependency is straightforward. Here’s how you can include it in your project:&lt;/p&gt;&lt;p&gt;&lt;br /&gt;
&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: repeat rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; overflow-wrap: normal; word-wrap: normal;"&gt;
implementation(libs.coil.compose)
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;By using this approach, there’s no need to specify the version manually, as it is automatically handled by the dependencies management system.&lt;/p&gt;&lt;p&gt;Alternatively, if you prefer to add the dependency directly with a specific version, you can use:&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: repeat rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; overflow-wrap: normal; word-wrap: normal;"&gt;
implementation("io.coil-kt:coil-compose:2.1.0")
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Either approach works, but managing dependencies via&amp;nbsp;&lt;code&gt;libs&lt;/code&gt;&amp;nbsp;can help maintain cleaner and more organized build scripts.&lt;/p&gt;&lt;h4&gt;&lt;br /&gt;&lt;/h4&gt;&lt;h4&gt;Switching from Local to Remote Images&lt;/h4&gt;&lt;p&gt;Previously, the images were stored locally in the app, and we were referencing them by their names. Now, we’ll modify the app to load images dynamically from URLs. The good news is that the process is straightforward using Coil, which integrates seamlessly with Jetpack Compose for loading images from the web.&lt;/p&gt;&lt;p&gt;However, one challenge arises when passing the image URL from the image list screen to the image details screen. Simply passing the URL directly as a parameter causes the app to crash with the following error:&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: repeat rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; overflow-wrap: normal; word-wrap: normal;"&gt;
java.lang.IllegalArgumentException: Navigation destination that matches request 
NavDeepLinkRequest{ uri=android-app://androidx.navigation/imageDetailsScreen
/image1/https://images.unsplash.com/photo-1506748686214-e9df14d4d9d0 } cannot 
be found in the navigation graph ComposeNavGraph(0x0) 
startDestination={Destination(0xa268bffc) route=login}.
&lt;/code&gt;
&lt;/pre&gt;&lt;div&gt;&lt;h4&gt;The Root Cause and the Solution&lt;/h4&gt;&lt;p&gt;This error occurs because URLs contain special characters that can cause issues when passed through navigation routes. Therefore, we can’t pass the URL as-is. The solution is to&amp;nbsp;&lt;strong&gt;encode&lt;/strong&gt;&amp;nbsp;the image URL before passing it between screens and then&amp;nbsp;&lt;strong&gt;decode&lt;/strong&gt;&amp;nbsp;it in the receiving screen. Both encoding and decoding are simple operations that are part of the Android SDK.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h4&gt;Updating the App&lt;/h4&gt;&lt;p&gt;To resolve the issue, we need to modify a few parts of the app:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;LoginApp&lt;/strong&gt;:&lt;br /&gt;The navigation routes need to be updated. In the previous implementation, we were passing only the image name to the image details screen and fetching the image from local storage. Now, we will pass both the image name and the encoded image URL to the image details screen.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Image List Screen&lt;/strong&gt;:&lt;br /&gt;In this screen, we will fetch the images from URLs using Coil. We’ll encode the URLs before passing them to the image details screen.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Image Details Screen&lt;/strong&gt;:&lt;br /&gt;Here, we will decode the URL to properly display the image using Coil, as well as handle the image name and other details.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;In the image list screen, I created a custom composable called "NetworkImage" and used it within the column.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: repeat rgb(240, 240, 240); border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; overflow-wrap: normal; word-wrap: normal;"&gt;
@Composable
fun NetworkImage(navController: NavController, imageName: String, url: String) {
    val painter = rememberAsyncImagePainter(url)
    Image(
        painter = painter,
        contentDescription = imageName,
        modifier = Modifier
            .fillMaxWidth()
            .height(200.dp)
            .padding(bottom = 16.dp)
            .clickable {
                navController.navigate("imageDetailsScreen/$imageName/${encodeUrl(url)}")
            },
        contentScale = ContentScale.Crop
    )
}

fun encodeUrl(rawUrl: String): String {
    return URLEncoder.encode(rawUrl, StandardCharsets.UTF_8.toString())
}

fun decodeUrl(encodedUrl: String): String {
    return URLDecoder.decode(encodedUrl, StandardCharsets.UTF_8.toString())
}
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;
&lt;/p&gt;&lt;div&gt;Here are few updated screen shots&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;1. Image list Screen&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7TwQTtWDQvAiot4y_h4z12KLNNYjbbR3ffF5kXu-CaVcKE_G9sZ3uxzRpX92RgQcBmdNjjEFuovHdqb8lL7AWFIQMnrmdutlKtTWebG95i3KojlgliKzA5kcAYNAbHvml0GM0H3MDaxf8quwCQezNOLpuCTijzqGvvwRVaEzh6ATgwQQdpBU0nxqvlAg/s1280/imageListScreen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1280" data-original-width="720" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7TwQTtWDQvAiot4y_h4z12KLNNYjbbR3ffF5kXu-CaVcKE_G9sZ3uxzRpX92RgQcBmdNjjEFuovHdqb8lL7AWFIQMnrmdutlKtTWebG95i3KojlgliKzA5kcAYNAbHvml0GM0H3MDaxf8quwCQezNOLpuCTijzqGvvwRVaEzh6ATgwQQdpBU0nxqvlAg/s320/imageListScreen.png" width="180" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;2. Image Details Screen&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFgbJAVWHuQGKCuDcRxravwVLiKWElN6195_ZhmJkn6FjYO78mYuu1avkuF33dU8RRqRhzv_pXe8Pnht1Fws4AJbjg9ELe-it7SRlXxzfWXX9GwleZT5N9kUTW5sYt4fGsGH8K9Z9OBZU7il57097C5bJaVPdtJKy75xId5KeCJkqLMJEUTKBoHMqL-II/s1280/imageDetailsScreen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1280" data-original-width="720" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiFgbJAVWHuQGKCuDcRxravwVLiKWElN6195_ZhmJkn6FjYO78mYuu1avkuF33dU8RRqRhzv_pXe8Pnht1Fws4AJbjg9ELe-it7SRlXxzfWXX9GwleZT5N9kUTW5sYt4fGsGH8K9Z9OBZU7il57097C5bJaVPdtJKy75xId5KeCJkqLMJEUTKBoHMqL-II/s320/imageDetailsScreen.png" width="180" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
Here is the complete &lt;a href="https://drive.google.com/file/d/17LnJ3YKRk-OYNnh421svQJH6QV4MIaJr/view"&gt;source code&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh7TwQTtWDQvAiot4y_h4z12KLNNYjbbR3ffF5kXu-CaVcKE_G9sZ3uxzRpX92RgQcBmdNjjEFuovHdqb8lL7AWFIQMnrmdutlKtTWebG95i3KojlgliKzA5kcAYNAbHvml0GM0H3MDaxf8quwCQezNOLpuCTijzqGvvwRVaEzh6ATgwQQdpBU0nxqvlAg/s72-c/imageListScreen.png" width="72"/><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Building My First Android Jetpack Compose Application: A Simple Multi-Screen App - Part - 1</title><link>http://smartandroidians.blogspot.com/2024/09/building-my-first-android-jetpack.html</link><category>Android</category><category>Jetpack compose</category><category>navigation</category><category>single activity</category><author>noreply@blogger.com (androidians)</author><pubDate>Sat, 28 Sep 2024 15:00:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-952216239099582530</guid><description>&lt;p&gt;&amp;nbsp;In this post, I’ll walk you through the creation of my first application using Jetpack Compose. This project was a great introduction to Compose, which simplifies UI development with a declarative approach. The app itself is fairly simple and contains a few key screens, demonstrating navigation, state handling, and passing data between screens.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h4&gt;Overview of the Application&lt;/h4&gt;&lt;p&gt;The app consists of four screens:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Login Screen&lt;/strong&gt;&lt;br /&gt;The journey starts with a basic login screen. Users enter their username and password here. Once they click on the&amp;nbsp;&lt;strong&gt;Submit&lt;/strong&gt;&amp;nbsp;button, the app navigates to the next screen, passing the username forward. At this stage, no backend validation is implemented as it’s a simple introductory app.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Welcome Screen&lt;/strong&gt;&lt;br /&gt;After a successful login, the user is greeted on the welcome screen. This screen displays a message that includes the username entered on the login screen, adding a personalized touch. Besides the welcome message, there's a button labeled&amp;nbsp;&lt;strong&gt;"Images"&lt;/strong&gt;. Clicking this button takes the user to the image list screen.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Image List Screen&lt;/strong&gt;&lt;br /&gt;The image list screen showcases a collection of images stored in the app’s&amp;nbsp;&lt;code&gt;drawable&lt;/code&gt;&amp;nbsp;folder. These images are displayed in a scrollable list, and each image is clickable. When a user selects an image, they are taken to the image details screen, where more information about the image is shown. In future updates, I plan to source images from URLs and fetch them dynamically. For now, keeping them local keeps things simple and manageable.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;strong&gt;Image Details Screen&lt;/strong&gt;&lt;br /&gt;On the image details screen, users can view the selected image in full along with its description. The screen includes a top app bar with a back arrow, making it easy for users to navigate back to the image list. The top bar also displays the image’s title for context.&lt;/p&gt;&lt;p&gt;Navigation between screens is smooth, and the back arrow ensures intuitive flow, allowing users to easily explore the list without getting lost.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;h4&gt;Data Handling&lt;/h4&gt;&lt;p&gt;All the data in this app, such as the username and image details, are stored locally within the app. This helps keep the app self-contained and fast without needing external dependencies. I’ve focused on keeping the data flow simple while demonstrating how state and UI interact in Jetpack Compose.&lt;/p&gt;&lt;h4&gt;What’s Next?&lt;/h4&gt;&lt;p&gt;This is just the beginning of what can be done with Jetpack Compose. In my next post, I’ll be enhancing this app by fetching images from URLs and adding more complex data handling scenarios. The goal is to continue exploring how Jetpack Compose makes Android development more streamlined, efficient, and modern.&lt;/p&gt;&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; An important point to mention is that we are not using Intents for navigation in this app. Additionally, data is passed between screens without the use of Intents or Bundles. The entire app operates within a single activity.&lt;/p&gt;&lt;p&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;Screenshots&lt;/b&gt;&lt;/p&gt;&lt;p&gt;&lt;b&gt;1) Login Screen&lt;/b&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAKnYUj3CcEevGgU8jOoMCvIYDYWMkzLhpJ9iF-aiFu351yt-Zqsuv5gSH5F1bhSuEDPxz3vPxCQ1GrcrCLCsO_cKs73_Yk8GvX2yTHscXB-15ng28QrKPD_bSiAxUTF6hKfsxaKYR96t53uZBogd9lAK8nZ-NGZTY-s4daEY6yDSEYJfxhIkapiPJWk8/s1280/login-screen-with-username.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1280" data-original-width="720" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAKnYUj3CcEevGgU8jOoMCvIYDYWMkzLhpJ9iF-aiFu351yt-Zqsuv5gSH5F1bhSuEDPxz3vPxCQ1GrcrCLCsO_cKs73_Yk8GvX2yTHscXB-15ng28QrKPD_bSiAxUTF6hKfsxaKYR96t53uZBogd9lAK8nZ-NGZTY-s4daEY6yDSEYJfxhIkapiPJWk8/s320/login-screen-with-username.png" width="180" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;Login Screen&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;b&gt;2) Welcome Screen&lt;/b&gt;&lt;/li&gt;&lt;b&gt;&lt;ol&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/ol&gt;&lt;br /&gt;&lt;/b&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwruDHlYiacea0lYcLtvgKLtfW1O0xktmy3rk4TGMbaB3Nen1V45BHUFPlVnEHXZy35ej6Zv11KTzRjfjDYA_LbQjsaoGjqqPtS9O9cutUjag74J82WivgmsT7194jOHZFZDfrd7Gp_Sfq4_4fxWl8yKsuHq4I8x_0kGRvIjxoulbkVpTwbxf77MVepFc/s1280/welcome-screen.png" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1280" data-original-width="720" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjwruDHlYiacea0lYcLtvgKLtfW1O0xktmy3rk4TGMbaB3Nen1V45BHUFPlVnEHXZy35ej6Zv11KTzRjfjDYA_LbQjsaoGjqqPtS9O9cutUjag74J82WivgmsT7194jOHZFZDfrd7Gp_Sfq4_4fxWl8yKsuHq4I8x_0kGRvIjxoulbkVpTwbxf77MVepFc/s320/welcome-screen.png" width="180" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;3) Image List Screen&lt;/b&gt;&lt;/ol&gt;&lt;ol&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpwMP5lIYa34Ak9G3WYop5rm5rjnznlby2_Q0Si1xYzOpA4aymXQVIg88QiXA8vl2BeBcUkWb1txDzoGDy7Nm57Pn504Jcm9Tr2P1PNz0-2tq1P9MI-UJkoB3HwFZT8AyULpPM-LWwjnHcVtZ04Slq5UMVmiXyVHhPnM_oB8I8Hvki6as2DRrglGU_ZEg/s1280/image-list-screen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1280" data-original-width="720" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjpwMP5lIYa34Ak9G3WYop5rm5rjnznlby2_Q0Si1xYzOpA4aymXQVIg88QiXA8vl2BeBcUkWb1txDzoGDy7Nm57Pn504Jcm9Tr2P1PNz0-2tq1P9MI-UJkoB3HwFZT8AyULpPM-LWwjnHcVtZ04Slq5UMVmiXyVHhPnM_oB8I8Hvki6as2DRrglGU_ZEg/s320/image-list-screen.png" width="180" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;/ol&gt;&lt;ol&gt;&lt;b&gt;4) Image Details Screen&lt;/b&gt;&lt;/ol&gt;&lt;ol&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT50WxvD8xigTJufYyDD5T0XanjO-GpjltU5-t_TWpjmxdfLkwbfPH0g7ksD20ihfDBHBidbEYiAMwsq2Xs6Dp2OCAmt8BhVkCDtvoMcl_u6pt1Zs607TgjkZHF7C6uejRRMdeBWCEOV77id9pqY_lcjHG72uFgy_dGU17B59Pz0nhSVV2LVIRhpr_bxc/s1280/image-details-screen.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1280" data-original-width="720" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjT50WxvD8xigTJufYyDD5T0XanjO-GpjltU5-t_TWpjmxdfLkwbfPH0g7ksD20ihfDBHBidbEYiAMwsq2Xs6Dp2OCAmt8BhVkCDtvoMcl_u6pt1Zs607TgjkZHF7C6uejRRMdeBWCEOV77id9pqY_lcjHG72uFgy_dGU17B59Pz0nhSVV2LVIRhpr_bxc/s320/image-details-screen.png" width="180" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;Full source code is available &lt;a href="https://drive.google.com/file/d/1pOEESFeOCh7ikUZ9H1rPwVQFKoUaOCrw/view" rel="nofollow" target="_blank"&gt;here&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAKnYUj3CcEevGgU8jOoMCvIYDYWMkzLhpJ9iF-aiFu351yt-Zqsuv5gSH5F1bhSuEDPxz3vPxCQ1GrcrCLCsO_cKs73_Yk8GvX2yTHscXB-15ng28QrKPD_bSiAxUTF6hKfsxaKYR96t53uZBogd9lAK8nZ-NGZTY-s4daEY6yDSEYJfxhIkapiPJWk8/s72-c/login-screen-with-username.png" width="72"/><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Discover the New Android Feature: Application Binary Interface (ABI) </title><link>http://smartandroidians.blogspot.com/2024/05/discover-new-android-feature.html</link><category>32-bit architecture</category><category>64-bit architecture</category><category>ABI</category><category>Application Binary Interface</category><category>pixel 7</category><author>noreply@blogger.com (androidians)</author><pubDate>Wed, 29 May 2024 08:45:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-7235538272743960190</guid><description>&lt;b&gt;A Quick History Lesson&lt;/b&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;
In the Android world, there are two main types of devices: those with 32-bit architecture and those with 64-bit architecture. Some devices can handle both, but the trend is moving towards a 64-bit only future. Take the Pixel 7, for example—it's a 64-bit only device. So why is this shift happening?&amp;nbsp;&lt;/div&gt;&lt;div&gt;Here are a few key reasons:&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Speed:&lt;/b&gt; 64-bit apps run faster. They can access extra registers and instructions that 32-bit apps can't.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Security:&lt;/b&gt; The larger address space of 64-bit systems enhances security measures like Address Space Layout Randomization (ASLR). Plus, the extra bits can help protect the integrity of control flow.
Memory&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Efficiency:&lt;/b&gt; Dropping 32-bit support can save up to 150 MB of RAM, which the OS previously needed even when 32-bit apps weren't running.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;What Are ABIs?&lt;/b&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Different Android devices have different CPUs, and each CPU supports specific instruction sets. Every combination of a CPU and its instruction set has its own Application Binary Interface (ABI).&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Want to check if your device is 64-bit? Look at the value of &lt;b&gt;&lt;i&gt;android.os.BUILD_SUPPORTED_ABIS. &lt;/i&gt;&lt;/b&gt;This will tell you if your device supports 64-bit architecture. Testing your apps on 64-bit devices is essential because that's where Android is headed, offering more power and efficiency.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Happy coding!


&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Simple way to show a list using compose - Call Log Details</title><link>http://smartandroidians.blogspot.com/2024/04/simple-way-to-show-list-using-compose.html</link><category>Android</category><category>CallLog</category><category>callLog type</category><category>Content Provider</category><category>date</category><category>Implicit Content Provider</category><category>incoming</category><category>Jetpack compose</category><category>missed</category><category>outgoing</category><category>permission flow</category><author>noreply@blogger.com (androidians)</author><pubDate>Mon, 29 Apr 2024 10:54:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-2646531243827600479</guid><description>&lt;p&gt;Without much ado, here is a simple way to show call log details.&lt;/p&gt;&lt;p&gt;I am breaking the functionality to few sub tasks as,&lt;/p&gt;&lt;p&gt;a) First get the permission to fetch call log from user&lt;/p&gt;&lt;p&gt;b) Then fetch the call log details from Implicit callLog content provider &amp;amp;&lt;/p&gt;&lt;p&gt;c) Finally show the list using Jetpack Compose.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Going to show the code snippet as well in 3 different sections same as subtask.&lt;/p&gt;&lt;p&gt;Fetching Permission from user. Here I am showing the comprehensive way to ask permission. Literally we can ask permission from user 2 times from app. If user denies the permission in all cases, then show the app info page from settings. I will show the complete code snippet here using RequestLauncher

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        // Check if the permission is already granted
        checkCallLogPermission()
}

private fun checkCallLogPermission() {
	if (ContextCompat.checkSelfPermission(
    	this,
        Manifest.permission.READ_CALL_LOG
	) != PackageManager.PERMISSION_GRANTED
   ) {
   // Permission is not granted, request it
   callLogPermissionLauncher.launch(Manifest.permission.READ_CALL_LOG)
	} else {
		// fetch details from call log content provider
		fetchCallLog()
	}
}

private val callLogPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted: Boolean -&gt;
	if (isGranted) {
		fetchCallLog()
	} else {
		if (shouldShowPermissionRationale(this, Manifest.permission.READ_CALL_LOG)) {
			checkCallLogPermission()
        } else {
            // show App Info page to allow permission
            openAppSettings(this)
		}
	}
}

private fun fetchCallLog() {
	setContent {
		MaterialTheme {
			DisplayCallLog()
		}
	}
}

fun shouldShowPermissionRationale(context: Activity, permission: String): Boolean {
	return ActivityCompat.shouldShowRequestPermissionRationale(context, permission)
}

fun openAppSettings(context: Context) {
  val intent = Intent().apply {
      action = android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
      data = Uri.fromParts("package", context.packageName, null)
  }
  context.startActivity(intent)
}
&lt;/code&gt;
&lt;/pre&gt;

After getting permission fetch call log details from implicit content provider. Here I am fetching details for last 10 days. Code snippet for the same is as follows,

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;

@Composable
fun DisplayCallLog() {
    val context = LocalContext.current
    var callLog by remember {
        mutableStateOf(emptyList&amp;lt;CallLogItem&amp;gt;())
    }
    // Check permission to read call logs
    val hasPermission = remember {
        ContextCompat.checkSelfPermission(
            context,
            Manifest.permission.READ_CALL_LOG
        ) == PackageManager.PERMISSION_GRANTED
    }

    if (hasPermission) {
        // Fetch call logs
        callLog = CallLogUtils.fetchCallLogData(contentResolver = context.contentResolver, daysToBeFetched = -10)
    }

    CallLogList(callLog)
}


fun fetchCallLogData(contentResolver: ContentResolver, daysToBeFetched: Int) : ArrayList&amp;lt;CallLogItem&amp;gt; {
	val callLogList = ArrayList&amp;lt;CallLogItem&amp;gt;()
	val projection = arrayOf(
		CallLog.Calls._ID,
        CallLog.Calls.NUMBER,
        CallLog.Calls.TYPE,
        CallLog.Calls.DATE,
        CallLog.Calls.DURATION
	)

	// Calculate the date - daysToBeFetched days ago
	val tenDaysAgo = DateUtils.addDays(daysToBeFetched)

	// Define the selection criteria
	val selection = "${CallLog.Calls.DATE} &gt; ?"

	// Define the selection arguments
	val selectionArgs = arrayOf(tenDaysAgo.toString())

	// Perform the query
	val cursor: Cursor? = contentResolver.query(
		CallLog.Calls.CONTENT_URI,  // The content URI of the call log provider
        projection,                 // The columns to retrieve
        selection,                  // Selection criteria (for last 10 days data)
        selectionArgs,              // Selection arguments (date 10 days ago)
        "${CallLog.Calls.DATE} DESC" // Sort order (by date in descending order)
	)

	// Process the cursor
	cursor?.use { cursorToProcess -&gt;
		val idColumnIndex = cursorToProcess.getColumnIndex(CallLog.Calls._ID)
		val numberColumnIndex = cursorToProcess.getColumnIndex(CallLog.Calls.NUMBER)
		val typeColumnIndex = cursorToProcess.getColumnIndex(CallLog.Calls.TYPE)
		val dateColumnIndex = cursorToProcess.getColumnIndex(CallLog.Calls.DATE)
		val durationColumnIndex = cursorToProcess.getColumnIndex(CallLog.Calls.DURATION)

		while (cursorToProcess.moveToNext()) {
			val id = cursorToProcess.getLong(idColumnIndex)
			val number = cursorToProcess.getString(numberColumnIndex)
			val type = cursorToProcess.getInt(typeColumnIndex)
			val date = cursorToProcess.getLong(dateColumnIndex)
			val duration = cursorToProcess.getLong(durationColumnIndex)

			val callType: String = when (type) {
				CallLog.Calls.INCOMING_TYPE -&gt; "Incoming"
				CallLog.Calls.OUTGOING_TYPE -&gt; "Outgoing"
				CallLog.Calls.MISSED_TYPE -&gt; "Missed"
				else -&gt; "Unknown"
			}

			// Process the call log data
			Log.d("CallLogData", "ID: $id, Number: $number, Type: $type, " +
				"Date: $date, Duration: $duration, callType: $callType")

			callLogList.add(CallLogItem(
				number,
				type,
				date,
				duration
			))
		}
	}


	// Close the cursor explicitly
	cursor?.close()
	return callLogList
}
    
fun addDays(days: Int) : Long {
	val calendar = Calendar.getInstance()
	calendar.add(Calendar.DAY_OF_YEAR, days)
	return calendar.timeInMillis
}

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

Finally the code snippet to show the list of callLog items using compose functions is as follows,

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;

@Composable
fun CallLogItem(callLogItem: CallLogItem) {
    Column(
        modifier = Modifier
            .padding(16.dp)
    ) {
        Text(text = "Number: ${callLogItem.phoneNumber}", fontWeight = FontWeight.Bold)
        Text(text = "Type: ${callLogItem.callLogType}")
        Text(text = "Date: ${callLogItem.date}")
        Text(text = "Duration: ${callLogItem.duration}")
    }
}

@Composable
fun CallLogList(callLog: List&amp;lt;CallLogItem&amp;gt;) {
    LazyColumn(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp)
    ) {
        items(callLog) { callLogItem -&gt;
            CallLogItem(callLogItem = callLogItem)
        }
    }
}

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

UI loks so simople and without any xml file. All code using kotlin itself. I am started enjoying using compose. I hope you guys also enjoying it.
&lt;br/&gt;&lt;br/&gt;
We can improve the UI using callLogType. According to the callLog type, we can change the color like for missed call, give red, outgoing give green and so on. Use the below code snippet to find the type of call log
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;val callType: String = when (callLogType) {
	CallLog.Calls.INCOMING_TYPE -&gt; "Incoming"
	CallLog.Calls.OUTGOING_TYPE -&gt; "Outgoing"
	CallLog.Calls.MISSED_TYPE -&gt; "Missed"
	else -&gt; "Unknown"
}
&lt;/code&gt;
&lt;/pre&gt;
And last but not least the dependencies used in the application are as follows,
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;dependencies {
    implementation 'androidx.core:core-ktx:1.8.0'
    implementation platform('org.jetbrains.kotlin:kotlin-bom:1.8.0')
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
    implementation 'androidx.activity:activity-compose:1.5.1'
    implementation platform('androidx.compose:compose-bom:2022.10.00')
    implementation 'androidx.compose.ui:ui'
    implementation 'androidx.compose.ui:ui-graphics'
    implementation 'androidx.compose.ui:ui-tooling-preview'
    implementation 'androidx.compose.material3:material3'
    implementation "androidx.compose.runtime:runtime-livedata"
}
&lt;/code&gt;
&lt;/pre&gt;

Happy Exploring!!
&lt;br/&gt;
Keep Rocking..
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Receive SMS using SmsRetriever in Android Kotlin </title><link>http://smartandroidians.blogspot.com/2024/01/receive-sms-using-smsretriever-in.html</link><category>Android</category><category>Android 13</category><category>BroadcastReceiver</category><category>code snippet</category><category>Kotlin</category><category>Regex</category><category>SMS</category><category>SmsRetriever</category><author>noreply@blogger.com (androidians)</author><pubDate>Mon, 1 Jan 2024 13:44:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-451642076441796445</guid><description>&lt;p&gt;In this article, I will explain how to receive SMS using SmsRetriever.&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;First we have to add the dependency,

&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;dependencies {
def playServicesVersion = 18.0.1
implementation 'com.google.android.gms:play-services-auth-api-phone:$playServicesVersion'
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Next step is add user consent to read OTP messages is as follows

&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;val task = SmsRetriever.getClient(context).startSmsUserConsent(senderPhoneNumber)
task.addOnSuccessListener {
// Successfully started SMS retriever
}

task.addOnFailureListener {
// Failed to start SMS retriever
}&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Now create a broadcast receiver in the activity and register it using &lt;b&gt;&lt;i&gt;registerReceiver()&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;private val smsReceiver = object : BroadcastReceiver() {
  override fun onReceive(context: Context, intent: Intent) {
  	if (SmsRetriever.SMS_RETRIEVED_ACTION == intent.action) {
    	val extras = intent.extras
        extras?.let {
          val status = it.get(SmsRetriever.EXTRA_STATUS) as Status
          when (status.statusCode) {
          	CommonStatusCodes.SUCCESS -&amp;gt; {
            val consentIntent = extras.getParcelable&amp;lt;Intent&amp;gt;(SmsRetriever.EXTRA_CONSENT_INTENT)
            try {
            	startActivityForResult(consentIntent, SMS_CONSENT_REQUEST)
            } catch (e: ActivityNotFoundException) {
            	Log.e("smsReceiver", "failed to show the SMS user consent dialog", e)
            }
            }
            CommonStatusCodes.TIMEOUT -&amp;gt; {
            	// take appropriate action here
            }
            else -&amp;gt; {}
         }
       }
    }
  }
}
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;Now register the receiver in activity &lt;b&gt;&lt;i&gt;onResume()&lt;/i&gt;&lt;/b&gt; using&lt;/p&gt;

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;val filter = IntentFilter(SmsRetriever.SMS_RETRIEVED_ACTION)
registerReceiver(smsReceiver, filter)
&lt;/code&gt;
&lt;/pre&gt;


&lt;p&gt;unregister the receiver in &lt;b&gt;&lt;i&gt;onPause()&lt;/i&gt;&lt;/b&gt; as well to avoid any leakage.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Now get the user consent response in &lt;b&gt;&lt;i&gt;onActivityResult(&lt;/i&gt;&lt;/b&gt;)&amp;nbsp;&lt;/p&gt;


&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
	super.onActivityResult(requestCode, resultCode, data)
    when (requestCode) {
    	SMS_CONSENT_REQUEST -&amp;gt;
	        if (resultCode == RESULT_OK) {
            	data?.let {
                	val message = it.getStringExtra(SmsRetriever.EXTRA_SMS_MESSAGE)
                    if (message != null ) {
                    	getOtp(message)
                    }
                }
            } else {
             // take appropriate action here as user didn't give permission to read OTP messages
            }
        
    }
}

private fun getOtp(msg: String) {
&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;// here the problem is all digits in the same will show here. Which cause issues so better to get the OTP using regex
&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;//val otp = message.filter { it.isDigit() } 
    // here OTP has 6 digits, so I am taking 6 digits which are coming together only
&lt;span&gt;&amp;nbsp;&amp;nbsp; &amp;nbsp;&lt;/span&gt;val pattern = "\\b\\d{6}\\b".toRegex()
    val matchResult = pattern.find(msg)
    if (matchResult != null) {
    	// val otp has the otp value that we got from SMS 
    	val otp = matchResult.value
    }
}&lt;/code&gt;&lt;br /&gt;&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;So, that's guys. Important thing to note here is no extra permission we require as READ_SMS and so on and all in all, it's a shorter and easier approach to read OTP messages. Hope you enjoyed the article&lt;/p&gt;&lt;p&gt;Happy exploring!!&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;

&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>How to check DND (Do Not Disturb) is turned on in Android</title><link>http://smartandroidians.blogspot.com/2023/12/how-to-check-dnd-do-not-disturb-is.html</link><category>Android</category><category>AudiManager</category><category>DND</category><category>Do Not Disturb</category><category>NotificationManager</category><author>noreply@blogger.com (androidians)</author><pubDate>Tue, 19 Dec 2023 16:00:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-4859433211806570918</guid><description>&lt;p&gt;Little hacks from my daily professional life sharing here. Utility function to check whether DND is turned on or not is as follows,

&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;fun isDndEnabled(context: Context): Boolean {
	if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.M) {
    	val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
        return notificationManager.currentInterruptionFilter == NotificationManager.INTERRUPTION_FILTER_NONE
    } else {
        // For devices below Android M
        val audioManager = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
        return audioManager.ringerMode == AudioManager.RINGER_MODE_SILENT
    }
}&lt;/code&gt;
&lt;/pre&gt;

&lt;p&gt;Here, for Android version M (API level 23) and above, we are using notificationManager to check DND is enabled or not. It uses `&lt;span style="background: rgba(var(--sk_foreground_min, 29, 28, 29), 0.04); color: #1d1c1d; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; orphans: 2; white-space: pre-wrap; widows: 2;"&gt;currentInterruptionFilter &lt;/span&gt;`property of notificationManager to check DND is enabled. If `&lt;span style="background-color: rgba(29, 28, 29, 0.04); caret-color: rgb(29, 28, 29); color: #1d1c1d; font-family: Monaco, Menlo, Consolas, &amp;quot;Courier New&amp;quot;, monospace; font-size: 12px; orphans: 2; white-space: pre-wrap; widows: 2;"&gt;currentInterruptionFilter &lt;/span&gt;` equals `NotificationManager.INTERRUPTION_FILTER_NONE` it means "Do Not Disturb" mode is on.&lt;/p&gt;&lt;p&gt;For Android version below M (API level 22 and below), we are using AuidoManager's ringerMode to check "Do Not Disturb" is enabled. If ringerMode equals `AudioManager.RINGER_MODE_SILENT` that means DND is enabled.&lt;/p&gt;&lt;p&gt;Happy Exploring!!!&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Communication between 2 different Android apps using Broadcast Receiver in Android 13 and above</title><link>http://smartandroidians.blogspot.com/2023/12/communication-between-2-different.html</link><category>Android</category><category>Android 13</category><category>between two apps</category><category>BroadcastReceiver</category><category>Communication</category><category>receiver</category><category>Security</category><category>Sender</category><author>noreply@blogger.com (androidians)</author><pubDate>Sat, 16 Dec 2023 20:20:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-7954153129353552866</guid><description>&lt;p&gt;Communication between 2 android apps can happen through various ways. If we want to share data in a structured way, then content provider is the way to go. If we want to launch an activity of another app, we can use intents with the package name of the other app. One thing to note here is the in manifest the activity should add &lt;b&gt;&lt;i&gt;android:exported = true.&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Here I am going to explain communication between 2 android apps using Broadcast Receiver. I will explain this in a step by step manner.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1. Firstly, I create 2 android apps one &lt;b&gt;&lt;i&gt;Sender&lt;/i&gt;&lt;/b&gt; and another &lt;b&gt;&lt;i&gt;Receiver&lt;/i&gt;&lt;/b&gt;.&lt;/p&gt;&lt;p&gt;2. From Sender app, I create an intent with an action. Then send a broadcast.&amp;nbsp;&lt;/p&gt;&lt;p&gt;3. This broadcast message will be received in receiver app through broadcast receiver. In receiver app, we have to declare the receiver with intent-filter. Where the action has to be defined. The action should be same as the once set in &lt;b style="font-style: italic;"&gt;Sender &lt;/b&gt;app. One thing to note here is, in manifest file, mark the receiver with &lt;b&gt;&lt;i&gt;android:exported=true&lt;/i&gt;&lt;/b&gt;&lt;/p&gt;&lt;p&gt;Note: The action defined should be unique otherwise conflict can occur and which leads to race condition. So it is better to create the action with the package name as prefix. and then provide a verb which defines the action, in this case, it is send.&lt;/p&gt;&lt;p&gt;eg:&amp;nbsp;&lt;span style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px;"&gt;com.androidians.receiver.ACTION_SEND_DATA_FROM_ANDROIDIANS&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;span style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;/p&gt;&lt;p&gt;Code snippets&lt;/p&gt;&lt;p&gt;1. Code snippet to send the broadcast message is as follows,

&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;val intent = Intent("com.androidians.receiver.ACTION_SEND_DATA_FROM_ANDROIDIANS")
intent.putExtra("data", "Hello from Sender")
sendBroadcast(intent)&lt;/code&gt;
&lt;/pre&gt;







&lt;p&gt;&amp;nbsp;2. Code snippet to declare the receiver in manifest is as follows,

&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;&amp;lt;receiver android:exported="true" android:name=".DataReceiver"&amp;gt;
&amp;lt;intent-filter&amp;gt;
&amp;lt;action android:name="com.androidians.receiver.ACTION_SEND_DATA_FROM_ANDROIDIANS"&amp;gt;
&amp;lt;/action&amp;gt;&amp;lt;/intent-filter&amp;gt;
&amp;lt;/receiver&amp;gt;&lt;/code&gt;
&lt;/pre&gt;
  
  
&lt;p&gt;3. Code Snippet for the Broadcast receiver is as follows,

&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import android.widget.Toast

class DataReceiver : BroadcastReceiver() {

    override fun onReceive(context: Context?, intent: Intent?) {
        Log.e("DataReceiver", intent?.getStringExtra("data")?: "")
        val msg = intent?.getStringExtra("data")?: ""
        Toast.makeText(context, "Message from sender, $msg", Toast.LENGTH_SHORT).show()
    }

}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;4. Registering receiver in Android 13 and above is little different. Code snippet to register and unregister receiver is as follows,

&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;private val dataReceiver = DataReceiver()
override fun onCreate(savedInstanceState: Bundle?) {
...
val intentFilter = IntentFilter("com.androidians.receiver.ACTION_SEND_DATA_FROM_ANDROIDIANS")
if (Build.VERSION.SDK_INT &amp;gt;= Build.VERSION_CODES.TIRAMISU) {
  registerReceiver(dataReceiver, intentFilter, RECEIVER_EXPORTED)
} else {
  registerReceiver(dataReceiver, intentFilter)
}
}

override fun onDestroy() {
  super.onDestroy()
  unregisterReceiver(dataReceiver)
}
&lt;/code&gt;&lt;/pre&gt;






&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;If we need to provide more security, we can add permission. That is one way to secure the data passed between the apps.&lt;/p&gt;&lt;p&gt;That's it.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Happy Exploring!!&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Android Jetpack Compose simple examples</title><link>http://smartandroidians.blogspot.com/2022/05/android-jetpack-compose-simple-examples.html</link><category>Android</category><category>buildFeatures</category><category>code snippet</category><category>gradle error</category><category>Jetpack compose</category><category>sample apps</category><author>noreply@blogger.com (androidians)</author><pubDate>Tue, 17 May 2022 14:20:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-1023230015636090354</guid><description>&lt;p&gt;&amp;nbsp;I just started with Jetpack compose. When I tried my first sample app, I got an error, so thought of share with you all. When I run the below code snippet
&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.compose.material.Text

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Text("Hello world!!")

        }
    }
}
&lt;/code&gt;
&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;My app crashed with the below error,

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;

java.lang.NoSuchMethodError: No static method setContent$default(Landroidx/activity/ComponentActivity;
Landroidx/compose/runtime/CompositionContext;Lkotlin/jvm/functions/Function0;ILjava/lang/Object;)V in 
class Landroidx/activity/compose/ComponentActivityKt; or its super classes (declaration of 
'androidx.activity.compose.ComponentActivityKt' appears in /data/app/

&lt;/code&gt;
&lt;/pre&gt;
The issue was in gradle file need to add buildFeatures,&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
buildFeatures {
   compose true
}
&lt;/code&gt;
&lt;/pre&gt;
Another change required in gradle file is,
  
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
composeOptions {
    kotlinCompilerExtensionVersion = "1.2.0-alpha08"
}
&lt;/code&gt;
&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;These are the 2 changes you have to made while building apps using Jetpack Compose, the modern toolkit for building native UI.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The screen shot for the same is,&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ4njteSq1R37qNA5DIvulH7M6eVs8ck4QFCXmD2qocYYWvs670wpTBwp2LziWHY8zMgphDU91vAu6bEhNgvDi0dTo4Oqg4W6EVzYOk6MYGNb77d9Nq5erEAjgEOFxRkbu6fSdoAgqWgknUEGn83hM2PlimbcinwRrsRbv_vFjYHTtdCpKrgS85a5y/s5120/Screenshot_20220517_141508.png" style="margin-left: auto; margin-right: auto;"&gt;&lt;img border="0" data-original-height="5120" data-original-width="2880" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ4njteSq1R37qNA5DIvulH7M6eVs8ck4QFCXmD2qocYYWvs670wpTBwp2LziWHY8zMgphDU91vAu6bEhNgvDi0dTo4Oqg4W6EVzYOk6MYGNb77d9Nq5erEAjgEOFxRkbu6fSdoAgqWgknUEGn83hM2PlimbcinwRrsRbv_vFjYHTtdCpKrgS85a5y/w180-h320/Screenshot_20220517_141508.png" title="Screen shot of Hello world app using Jetpack Compose" width="180" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Screen shot of Hello world app using Jetpack Compose&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;One last thing before winding up is the dependencies I used are,
  
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
// compose
implementation 'androidx.activity:activity-compose:1.4.0'
// compose material design
implementation 'androidx.compose.material:material:1.1.1'
&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div&gt;That's it.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Happy exploring!!&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJ4njteSq1R37qNA5DIvulH7M6eVs8ck4QFCXmD2qocYYWvs670wpTBwp2LziWHY8zMgphDU91vAu6bEhNgvDi0dTo4Oqg4W6EVzYOk6MYGNb77d9Nq5erEAjgEOFxRkbu6fSdoAgqWgknUEGn83hM2PlimbcinwRrsRbv_vFjYHTtdCpKrgS85a5y/s72-w180-h320-c/Screenshot_20220517_141508.png" width="72"/><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Create Password protected zip file in mac terminal</title><link>http://smartandroidians.blogspot.com/2022/05/create-password-protected-zip-file-in.html</link><category>command</category><category>mac</category><category>password protected zip file</category><category>terminal</category><category>zip</category><author>noreply@blogger.com (androidians)</author><pubDate>Thu, 12 May 2022 21:24:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-7224529838428853974</guid><description>&lt;p&gt;&amp;nbsp;To create password protected zip file in Mac use this command,&lt;/p&gt;
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
zip -e destination-file.zip source-file.extension
eg:
zip -e password.zip password.txt
&lt;/code&gt;
&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;

After entering the command, terminal prompts to enter the password, enter the password and verifies it. That's it.&amp;nbsp;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&amp;nbsp;Happy exploring!!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Error: Remote repository not found</title><link>http://smartandroidians.blogspot.com/2022/05/error-remote-repository-not-found.html</link><category>Android</category><category>GitHub repository</category><category>remote repository not found</category><author>noreply@blogger.com (androidians)</author><pubDate>Wed, 11 May 2022 08:52:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-561628804679318858</guid><description>&lt;p&gt;I am trying to clone a github repo from mac terminal and got the error as "remote repository not found". Later I found, we have to pass the userName while clone the github repo. There is a slight modification in the command. The modified command is as follows,&lt;/p&gt;&lt;p&gt;&lt;span style="background-color: white; color: #232629; font-family: inherit; font-size: 15px; font-style: inherit; font-variant-caps: inherit; orphans: 2; widows: 2;"&gt;
&lt;/span&gt;&lt;/p&gt;

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;   
 eg. git clone https://user@github.com/fooo/foo.git
&lt;/code&gt;
&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;

Happy Exploring!!&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Not able to build android project with butterknife library</title><link>http://smartandroidians.blogspot.com/2022/05/not-able-to-build-android-project-with.html</link><category>Android</category><category>butterknife</category><category>gradle</category><category>jdk</category><author>noreply@blogger.com (androidians)</author><pubDate>Tue, 10 May 2022 05:56:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-7756754955743820141</guid><description>&lt;p&gt;As my current project still has dependency to butterknife in gradle file. I am not using it but need to keep it as part of the legacy code we have in the project and I got a strange error like&lt;/p&gt;&lt;p&gt;

&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;  
cause: superclass access check failed: class butterknife.compiler.butterknifeprocessor$rscanner 
(in unnamed module) cannot access class com.sun.tools.javac.tree.treescanner (in module jdk.compiler) 
because module jdk.compiler does not export com.sun.tools.javac.tree to unnamed module
&lt;/code&gt;
&lt;/pre&gt;
&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;It bugged my head and later I found the solution, it is in gradle use the JDK version 11 instead of 16. I was using JDK16, which causes the error. When I modified to JDK 11, it all started working fine.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Happy exploring!!&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Publish Android Library Artifacts to Amazon S3 bucket</title><link>http://smartandroidians.blogspot.com/2022/04/publish-android-library-artifacts-to.html</link><category>Amazon aws s3 bucket</category><category>Android</category><category>Android library</category><category>Android library artifacts</category><category>gradle</category><category>gradlew</category><category>Maven</category><category>Maven publish plugin</category><category>MavenLocal</category><author>noreply@blogger.com (androidians)</author><pubDate>Sun, 17 Apr 2022 12:15:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-3603447885798699191</guid><description>&lt;p&gt;Even though we can use &lt;a href="http://jitpack.io"&gt;Jetpack&lt;/a&gt;&amp;nbsp;for hosting public and private repositories, I am not happy with it especially when publishing private repositories.&amp;nbsp;&lt;/p&gt;&lt;p&gt;The solution that I come across is to use &lt;a href="https://docs.gradle.org/current/userguide/publishing_maven.html#publishing_maven:install"&gt;MavenLocal&lt;/a&gt;&amp;nbsp;task from the maven publish plugin and then publish &amp;nbsp;the Android library artifacts to Amazon S3 bucket.&amp;nbsp;&lt;/p&gt;&lt;p&gt;The step by step procedure to publish Android library artifacts to Amazon S3 buckets using maven plblish plugin is as follows,&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;1. Firstly create a IAM user in Amazon aws. So that if anything goes wrong there is limited exposure.&lt;/p&gt;&lt;p&gt;2. Now create secretKey and secretAccessKey for the user, basically user credentials need to be generated. This AwsCredentials we have to use in the gradle file of android library. So keep it handy for future usage.&lt;/p&gt;&lt;p&gt;3. &amp;nbsp;Then setup the amazon s3 bucket, where we will place the android library artifacts like, aar, pom file, sha1, md5 and so on. So that is it. Amazon aws part is done. Now move to our android library gradle file.&lt;/p&gt;&lt;p&gt;4. Here, initllay maven publish plugin has to be added. Sample code snippet is,


&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
apply plugin: 'com.android.library'
apply plugin: 'maven-publish'
&lt;/code&gt;
&lt;/pre&gt;


5. Now add the group name and version as follows,

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
group = 'com.companyname.library-name'
version='1.0.0'
&lt;/code&gt;
&lt;/pre&gt;
6. Now, add sources as an artifact as follows,

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
task sourceJar(type: Jar) {
  from android.sourceSets.main.java.srcDirs
  classifier "sources"
}
&lt;/code&gt;
&lt;/pre&gt;
7. After this, loop over all variants,

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
android.libraryVariants.all { variant -&amp;gt;
  variant.output.all { output -&amp;gt;
    publishing.publications.create(variant.name, MavenPublication) {
       artifact sourceJar
       artifact source:output.outputFile, classifier: output.fileName
    }
  }
  pom.withXml {
   .............
  }
}
&lt;/code&gt;
&lt;/pre&gt;
8. Ensure that publish task depends on assembly.

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;

tasks.all {
  if (task instanceOf AbstractpublishToMaven) {
     task.dependsOn assemble
  }
}

&lt;/code&gt;
&lt;/pre&gt;
&lt;div&gt;&lt;br /&gt;





&lt;div&gt;9. Configure the destination repository with S3 URL and add aws IAM user credentials
  
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
publishing {
   repositories {
   	  maven {
         url BUCKET-NAME.s3.amazonaws.com
         credentials (AwsCredentials) {
            accessKey ....
            secretKey ....
         }
      }
   }
}
&lt;/code&gt;
&lt;/pre&gt;
  
  
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;10. Finally, run th epublish task in the terminal as,
  
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;

./gradlew libraryName:publish

&lt;/code&gt;
&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
11. Not over yet, the last point is, to use the library in other project, add the below code snippet in root gradle file

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
allProjects {
  repositories {
     google()
     Jcenter()
     maven {
       url BUCKET-NAME.s3.amazonaws.com"
      credentials (AwsCredentials) {
         accessKey "..."
        secretkey "....."
      }
     }
}
&lt;/code&gt;
&lt;/pre&gt;
  
&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;So, that is it guys.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Keep Exploring!!!&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Tracks the screen view events in Android</title><link>http://smartandroidians.blogspot.com/2022/04/tracks-screen-view-events-in-android.html</link><category>Activity</category><category>analytics</category><category>Android</category><category>baseActivity</category><category>baseApplication</category><category>Fragment</category><category>registerActivityLifecycleCallbacks</category><category>registerFragmentLifecycleCallbacks</category><category>screenView events</category><category>track</category><author>noreply@blogger.com (androidians)</author><pubDate>Sun, 10 Apr 2022 15:50:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-5205498706010003930</guid><description>&lt;p&gt;As you all know screen in Android can be either an activity or a fragment. So say, you want to track the screen view events from SDK you created, you can do so using,&amp;nbsp;&lt;span style="background-color: #2b2b2b; color: #a9b7c6; font-family: &amp;quot;JetBrains Mono&amp;quot;, monospace; font-size: 9.8pt;"&gt;registerActivityLifecycleCallbacks()&lt;/span&gt;`&amp;amp;&lt;/p&gt;&lt;p&gt;&lt;span style="background-color: #2b2b2b; color: #a9b7c6; font-family: &amp;quot;JetBrains Mono&amp;quot;, monospace; font-size: 9.8pt;"&gt;registerFragmentLifecycleCallbacks()&lt;/span&gt;&amp;nbsp;&lt;/p&gt; These two methods come handy to register the screen view events for analytics purpose. Similar like how firebase track the screen view events.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Here, things to note is registerActivityLfecycleCallbacks() has to be called from the application class and registerFragmentLifecycleCallbacks() has to be called from say the baseActivity(if you have one in your project) or else in the activities that you wish to track.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The code snippet for the same is as follows,&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;To register the callback for activity, add it in application base class as,&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;class BaseApplication : Application() {

    override fun onCreate() {
        super.onCreate()
        registerActivityLifecycleCallbacks(ViewLifecycleCallbacks())
    }

}&lt;/code&gt;
&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
To register for fragment callbacks, add in base activity or in which ever activity you are interested in,&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity

class BaseActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        supportFragmentManager.registerFragmentLifecycleCallbacks(ViewLifecycleCallbacks(), true)
    }
}
&lt;/code&gt;
&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;

Now, finally our ViewLifecycleCallbacks class is as follows,
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;class ViewLifecycleCallbacks : Application.ActivityLifecycleCallbacks, FragmentManager.FragmentLifecycleCallbacks() {

    // activity callbacks
    override fun onActivityCreated(p0: Activity, p1: Bundle?) {
        TODO("Not yet implemented")
    }
    ......
    // fragments callbacks
    override fun onFragmentPreAttached(fm: FragmentManager, f: Fragment, context: Context) {
        super.onFragmentPreAttached(fm, f, context)
    }
    ......
&lt;/code&gt;
&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;Happy Exploring!!!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Creating hash in Android</title><link>http://smartandroidians.blogspot.com/2022/04/creating-hash-in-android.html</link><category>Android</category><category>hashing function</category><category>MD5</category><category>MessageDigest</category><category>SHA-256</category><author>noreply@blogger.com (androidians)</author><pubDate>Sat, 9 Apr 2022 14:03:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-2780039832177330306</guid><description>&lt;p&gt;There are various algorithms to create hash value. Some of them are MD5, SHA1, SHA256 and more. The best way to create hash value is using SHA256 beacuse it create a long output so that collision will be minimal.&lt;/p&gt;&lt;p&gt;The code snippet for the hashing valuse using SHA-256 is as,&lt;/p&gt;
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;private fun createHash(s: String) : String {
        val messageDigest = MessageDigest.getInstance("SHA-256")
        messageDigest.update(s.toByteArray(Charsets.UTF_8))
        return String(messageDigest.digest())
}    &lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;div&gt;So that is all for today.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Happy exploring!!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Issue with Room and Mac OS</title><link>http://smartandroidians.blogspot.com/2022/01/issue-with-room-and-mac-os.html</link><category>Android</category><category>android studio</category><category>embedded JDK</category><category>mac os</category><category>Room</category><author>noreply@blogger.com (androidians)</author><pubDate>Fri, 28 Jan 2022 05:59:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-4734947218591793211</guid><description>&lt;p&gt;&amp;nbsp;This is a long disturbing problem for me. Whenever I am using a simple android project with room, build is failing. I was literally fed up with the issue. Finally what I did is, I wake up early morning and try to find the exact solution and it seems with the Mac OS, not with the code. First the error is as follows,&lt;/p&gt;
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':app:kaptDebugKotlin'.
&amp;gt; A failure occurred while executing org.jetbrains.kotlin.gradle.internal.KaptWithoutKotlincTask$KaptExecutionWorkAction
   &amp;gt; java.lang.reflect.InvocationTargetException (no error message)

&lt;/code&gt;
&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;
The solution for this is we have to use the embedded JDK that comes along with the Android Studio. I am attaching screen shot for reference.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/a/AVvXsEjVfjK9cY0tEuOLNo-vMGn5zas5Z-GjGDWXTibiXIGbHVeUuVfYsglhIhVe8vTkelhW7m123WkosJ4IIv7ZCkF_Jhv_hZZ7WHAb0PDQ4_NSgrOjGYMEYGRiO0k_EqFCtN4gj49u_HbFYdQIWUCrYjS6_OGIQmq4vJXWniXOixw0i9HwKl8J4weCsdKe=s2044" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" data-original-height="1142" data-original-width="2044" height="358" src="https://blogger.googleusercontent.com/img/a/AVvXsEjVfjK9cY0tEuOLNo-vMGn5zas5Z-GjGDWXTibiXIGbHVeUuVfYsglhIhVe8vTkelhW7m123WkosJ4IIv7ZCkF_Jhv_hZZ7WHAb0PDQ4_NSgrOjGYMEYGRiO0k_EqFCtN4gj49u_HbFYdQIWUCrYjS6_OGIQmq4vJXWniXOixw0i9HwKl8J4weCsdKe=w640-h358" width="640" /&gt;&lt;/a&gt;&lt;/div&gt;&lt;br /&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Happy exploring!!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/a/AVvXsEjVfjK9cY0tEuOLNo-vMGn5zas5Z-GjGDWXTibiXIGbHVeUuVfYsglhIhVe8vTkelhW7m123WkosJ4IIv7ZCkF_Jhv_hZZ7WHAb0PDQ4_NSgrOjGYMEYGRiO0k_EqFCtN4gj49u_HbFYdQIWUCrYjS6_OGIQmq4vJXWniXOixw0i9HwKl8J4weCsdKe=s72-w640-h358-c" width="72"/><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Wallpaper in Android</title><link>http://smartandroidians.blogspot.com/2022/01/wallpaper-in-android.html</link><category>Android</category><category>Wallpaper</category><category>Wallpapermanager</category><author>noreply@blogger.com (androidians)</author><pubDate>Wed, 19 Jan 2022 17:17:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-80603479121524591</guid><description>&lt;p&gt;Setting wallpaper in android is pretty simple, just two lines of code. First get the instance of wallpaper manager and using the API setResource() is one of the way to set wallpaper from resources and I think, it is the simplest. The code snippet is,&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
override fun onCreate(savedInstanceState: Bundle?) {
  super.onCreate(savedInstanceState)
  setContentView(R.layout.activity_main)

  val wallpaperMgr = WallpaperManager.getInstance(applicationContext)
  findViewById&amp;lt;Button&amp;gt;(R.id.setWallpaperBtn).setOnClickListener {
     wallpaperMgr.setResource(R.drawable.img)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;/p&gt;

You can also use other methods like setBitmap(), to set wallpaper &amp;nbsp;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Happy Exploring!!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Resize Bitmap in Android without losing much quality</title><link>http://smartandroidians.blogspot.com/2021/12/resize-bitmap-in-android-without-losing.html</link><category>Android</category><category>bitmap</category><category>createdScaledBitmap</category><category>matrix</category><category>resize</category><author>noreply@blogger.com (androidians)</author><pubDate>Thu, 23 Dec 2021 12:12:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-4389942421215904521</guid><description>&lt;p&gt;&amp;nbsp;Without losing much of quality to scale bitmap, its better to use "matrix" rather than createScaledBitmap()&lt;/p&gt;
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
private static Bitmap getResizedBitmap(Bitmap bitmap, int newWidth, int newHeight, boolean keepOriginal) {
        int width = bitmap.getWidth();
        int height = bitmap.getHeight();
        float scaleWidth = ((float) newWidth) / width;
        float scaleHeight = ((float) newHeight) / height;
        
        Matrix matrix = new Matrix();        
        matrix.postScale(scaleWidth, scaleHeight);
        Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false);
        if(!keepOriginal){
            bitmap.recycle();
        }
        return resizedBitmap;
}
&lt;/code&gt;
&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;

Resizing bitmap using "createScaledBitmap()"

&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
public static Bitmap getResizedBitmap(Bitmap image, int maxSize) {
	int width = image.getWidth();
	int height = image.getHeight();
	float bitmapRatio = (float) width / (float) height;
	if (bitmapRatio &amp;gt; 1) {
		width = maxSize;
		height = (int) (width / bitmapRatio);
	} else {
		height = maxSize;
		width = (int) (height * bitmapRatio);
	}
	return Bitmap.createScaledBitmap(image, width, height, true);
}
&lt;/code&gt;
&lt;/pre&gt;While using createScaledBitmap(), put the last param as true because it will do the bilinear filtering for resampling to prevent aliasing.&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Happy exploring!!&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>To prevent screen capture in Android </title><link>http://smartandroidians.blogspot.com/2021/12/to-prevent-screen-capture-in-android.html</link><category>Android</category><category>Kotlin</category><category>onCreate</category><category>prevent screen capture</category><category>window</category><author>noreply@blogger.com (androidians)</author><pubDate>Tue, 7 Dec 2021 17:01:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-6839385145220365716</guid><description>&lt;p&gt;&amp;nbsp;To prevent screen capture in Android is pretty easy. It is just one line code. The code snippet in kotlin is
&lt;/p&gt;&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    window.setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE)
}  
&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;

The code snippet in java is as follows,
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}  
&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;

When you try to take a screen shot you will get a notification like this,&lt;div&gt;&lt;br /&gt;&amp;nbsp;

&lt;table align="center" cellpadding="0" cellspacing="0" class="tr-caption-container" style="margin-left: auto; margin-right: auto;"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td style="text-align: center;"&gt;&lt;a href="https://blogger.googleusercontent.com/img/a/AVvXsEiB7apic5mH2-EgEIRssBJkA_zCu7GCD-R5CFUIs6xceLsd1PIzh9p9RWuL3Olji_MIy2qiLPiQ_SFD2XQkCJc71I9o6zj9_VWGdbgpgVu-mlf0lUGGW5rzuOxQ3B4bJp3lWi0GWQVfvEObr1D0i7sXcun_sbvUVdymmWR7VDyKkGJ1d9RzbsB6gZcV=s1882" style="margin-left: auto; margin-right: auto;"&gt;&lt;img alt="Notification when you try to take a screen shot" border="0" data-original-height="1882" data-original-width="802" height="640" src="https://blogger.googleusercontent.com/img/a/AVvXsEiB7apic5mH2-EgEIRssBJkA_zCu7GCD-R5CFUIs6xceLsd1PIzh9p9RWuL3Olji_MIy2qiLPiQ_SFD2XQkCJc71I9o6zj9_VWGdbgpgVu-mlf0lUGGW5rzuOxQ3B4bJp3lWi0GWQVfvEObr1D0i7sXcun_sbvUVdymmWR7VDyKkGJ1d9RzbsB6gZcV=w272-h640" width="272" /&gt;&lt;/a&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td class="tr-caption" style="text-align: center;"&gt;Notification appears when you try to take a screen shot&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;br /&gt;

&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Happy exploring!!&lt;/p&gt;&lt;p&gt;&lt;/p&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" height="72" url="https://blogger.googleusercontent.com/img/a/AVvXsEiB7apic5mH2-EgEIRssBJkA_zCu7GCD-R5CFUIs6xceLsd1PIzh9p9RWuL3Olji_MIy2qiLPiQ_SFD2XQkCJc71I9o6zj9_VWGdbgpgVu-mlf0lUGGW5rzuOxQ3B4bJp3lWi0GWQVfvEObr1D0i7sXcun_sbvUVdymmWR7VDyKkGJ1d9RzbsB6gZcV=s72-w272-h640-c" width="72"/><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>MaterialCardView in Android</title><link>http://smartandroidians.blogspot.com/2021/11/materialcardview-in-android.html</link><category>Android</category><category>MaterialCardView</category><category>ripple effect</category><author>noreply@blogger.com (androidians)</author><pubDate>Sat, 20 Nov 2021 18:14:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-8896669632309551279</guid><description>&lt;p&gt;&amp;nbsp;If materialCardView is the root element, then clickable and focusable to true is not necessary, but when it materialCardView is a child element then for making the ripple effect when clicking on the card we have to explicitly mention clickable and focusable to true for the ripple effect when clicked on the card.&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;The code snippet is,&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&amp;lt;com.google.android.material.card.MaterialCardView&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt;&lt;/b&gt;android:background="?android:attr/selectableItemBackground"&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt;android:clickable="true"&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;span class="Apple-tab-span" style="white-space: pre;"&gt;	&lt;/span&gt;android:focusable="true"&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;……&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;/&amp;gt;&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;Happy exploring!!!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Coroutines Basics and Structured Concurrency in Coroutines</title><link>http://smartandroidians.blogspot.com/2021/09/coroutines-basics-and-structured.html</link><category>Android</category><category>async</category><category>await</category><category>coroutine builder</category><category>coroutines</category><category>Kotlin</category><category>launch</category><category>structured concurrency</category><author>noreply@blogger.com (androidians)</author><pubDate>Sun, 26 Sep 2021 12:48:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-3316401156019960221</guid><description>&lt;div style="text-align: left;"&gt;&lt;b&gt;What is coroutines?&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;A&amp;nbsp;&lt;i&gt;coroutine&lt;/i&gt;&amp;nbsp;is an instance of suspendable computation.&amp;nbsp;Conceptually it is similar to a thread, in the sense that it takes a block of code to run that works concurrently with the rest of the code.&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;However, a coroutine is not bound to any particular thread. It may suspend its execution in one thread and resume in another one.&lt;/div&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px; text-align: left;"&gt;&lt;br /&gt;&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;Coroutine builders&lt;/b&gt;&lt;/p&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;b&gt;launch() -&lt;/b&gt; is a coroutine builder. It launches a new coroutine concurrently with the rest of the code, which continues to work independently.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;b&gt;delay() -&lt;/b&gt; is a special coroutine function. It suspends the coroutine for a specific time. Suspending a coroutine does not delay the underlying thread it created.&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;b&gt;runBlocking() -&lt;/b&gt; is also a coroutine builder that bridges the non coroutine world with coroutine world - RunBlocking means that the thread that it runs will get blocked for the duration of the call, means until all coroutines inside runBlocking {} complete their execution.&lt;br /&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;b&gt;async()&lt;/b&gt; is another coroutine builder, that we will see later.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px; text-align: left;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px; text-align: left;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-stretch: normal; line-height: normal; margin: 0px; text-align: left;"&gt;&lt;b&gt;Structured Concurrency&lt;/b&gt;&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;&lt;div style="text-align: left;"&gt;Coroutines follows a principle called structured concurrency, which means a new coroutine can only be launched in a coroutineScope, which determines the lifetime of the coroutine.&lt;br /&gt;An outer scope cannot complete until all its children coroutines complete&lt;br /&gt;Structured concurrency also ensures that any error are properly reported and never lost.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;eg:)&lt;/div&gt;&lt;/div&gt;
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
fun main() = runBlocking { // this: CoroutineScope
    launch { // launch a new coroutine and continue
        delay(1000L) // non-blocking delay for 1 second (default time unit is ms)
        println("World!") // print after delay
    }
    println("Hello") // main coroutine continues while a previous one is delayed
}
&lt;/code&gt;
&lt;/pre&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/p&gt;&lt;div style="text-align: left;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;b&gt;Parallel decomposition&lt;/b&gt;&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;Sample code that shows how to load two images in parallel and combine them later&lt;/div&gt;&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px; text-align: left;"&gt;&lt;br /&gt;&lt;/p&gt;
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;suspend fun &lt;/b&gt;loadAndCombine(name1: String, name2: String): Image {&amp;nbsp;&lt;/p&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&amp;nbsp; &amp;nbsp; &lt;b&gt;val &lt;/b&gt;deferred1 = &lt;i&gt;async&lt;/i&gt; &lt;b&gt;{ &lt;/b&gt;&lt;i&gt;loadImage&lt;/i&gt;(name1) &lt;b&gt;}&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; val &lt;/b&gt;deferred2 = &lt;i&gt;async&lt;/i&gt; &lt;b&gt;{ &lt;/b&gt;&lt;i&gt;loadImage&lt;/i&gt;(name2) &lt;b&gt;}&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; return &lt;/b&gt;&lt;i&gt;combineImages&lt;/i&gt;(deferred1.await(), deferred2.await())&lt;/p&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;}&lt;/p&gt;&lt;/code&gt;
&lt;/pre&gt;&lt;div style="text-align: left;"&gt;Unfortunately, this example is wrong on many levels&lt;br /&gt;The suspending function&amp;nbsp;loadAndCombine, itself, is going to be called from inside a coroutine. What if that operation gets cancelled? Then loading of both images still proceeds unfazed. That is not what we want from a reliable code&lt;br /&gt;With structured concurrency&amp;nbsp;async&amp;nbsp;coroutine builder became an extension on&amp;nbsp;CoroutineScope&amp;nbsp;just like&amp;nbsp;launch&amp;nbsp;did. You cannot simply write&amp;nbsp;async { … }&amp;nbsp;anymore, you have to provide a scope.&amp;nbsp;&lt;br /&gt;&lt;br /&gt;A proper example of parallel decomposition becomes:&lt;/div&gt;



&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px; text-align: left;"&gt;&lt;b&gt;suspend fun &lt;/b&gt;loadAndCombine(name1: String, name2: String): Image =&lt;/p&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&amp;nbsp; &amp;nbsp; &lt;i&gt;coroutineScope&lt;/i&gt; &lt;b&gt;{&lt;/b&gt;&amp;nbsp;&lt;/p&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;b&gt;val &lt;/b&gt;deferred1 = &lt;i&gt;async&lt;/i&gt; &lt;b&gt;{ &lt;/b&gt;&lt;i&gt;loadImage&lt;/i&gt;(name1) &lt;b&gt;}&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; val &lt;/b&gt;deferred2 = &lt;i&gt;async&lt;/i&gt; &lt;b&gt;{ &lt;/b&gt;&lt;i&gt;loadImage&lt;/i&gt;(name2) &lt;b&gt;}&lt;/b&gt;&lt;/p&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&lt;b&gt;&amp;nbsp; &amp;nbsp; &amp;nbsp; &amp;nbsp; &lt;/b&gt;&lt;i&gt;combineImages&lt;/i&gt;(deferred1.await(), deferred2.await())&lt;/p&gt;
&lt;p style="font-family: &amp;quot;Helvetica Neue&amp;quot;; font-size: 13px; font-stretch: normal; line-height: normal; margin: 0px;"&gt;&amp;nbsp;&lt;b&gt;}&lt;/b&gt;&lt;/p&gt;&lt;/code&gt;
&lt;/pre&gt;&lt;div style="text-align: left;"&gt;You have to wrap your code into&amp;nbsp;coroutineScope { … }&amp;nbsp;block that establishes a boundary of your operation, its scope.&amp;nbsp;&lt;br /&gt;All the&amp;nbsp;async&amp;nbsp;coroutines become the children of this scope and, if the scope fails with an exception or is cancelled, all the children are cancelled, too.&lt;/div&gt;&lt;div style="text-align: left;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;Coroutines vs Threads&lt;/b&gt;&lt;/div&gt;&lt;div&gt;&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/div&gt;&lt;div&gt;Threads are heavy or expensive. We are told that threads are expensive resources and one should not go around creating them left and right. A well written program usually creates a thread pool at startup and then uses it to offload various computations.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But the story is different with coroutines. it is very convenient to create coroutines since they are so cheap.&amp;nbsp;&lt;/div&gt;&lt;div&gt;Coroutines are always related to some local scope in our application, which is an entity with a limited life-time, like a UI element. We rarely launch coroutines "globally" like we do with threads. When launching coroutines globally, we have to explicitly declare the global scope as, GlobalScope.launch()&lt;/div&gt;&lt;div&gt;&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;div style="text-align: left;"&gt;Happy exploring!!!&lt;/div&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>How to investigate ANR (Application Not Responding) in Android</title><link>http://smartandroidians.blogspot.com/2021/09/how-to-investigate-anr-application-not.html</link><category>5 sec</category><category>5 seconds</category><category>adb</category><category>Android</category><category>ANR</category><category>application not responding</category><category>traces.txt</category><author>noreply@blogger.com (androidians)</author><pubDate>Fri, 24 Sep 2021 13:11:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-8266431246873685148</guid><description>&lt;p&gt;You may already know ANR occurs when we do long running operation on our main or UI thread. For example, on click of the button do a operation like download an image over the network, say it took more than 5 seconds. Then you may feel like UI gets freeze and application not responding. Basically any task which took more than 5 seconds on the main thread cause ANR.&amp;nbsp;&lt;/p&gt;&lt;p&gt;Unlike crashes, finding the exact cause of ANR is difficult, as logical may not have the line number and file name to say, where exactly app is having the issue.&lt;/p&gt;&lt;p&gt;What need to do is,&lt;/p&gt;&lt;p&gt;1. First get the /data/anr/traces.txt from the device which happened using the add command&lt;/p&gt;
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
  adb shell "cd /data/anr &amp;amp;&amp;amp; cat traces.txt" &amp;gt; anr.txt

&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Now, we copy the traces.txt to anr.txt. search for "waiting to lock", there could be potential issue around these lines. You will get the fileName and lineNumber in these area.&amp;nbsp;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Another way to get the traces.txt is by&amp;nbsp;&lt;/div&gt;&lt;div&gt;
&lt;pre style="background-color: #f0f0f0; background-image: URL(https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgAlvRb60PPRFJVaiF8Gco7UQ8GehreWh1QExtTatGfoC0SMTp-5efcgQvZ7Lv5kOdprRkl7VhxOul8ZJTFUt08HIwvg7cZfu67WTMs4rFuui7IQSUCOWyXlnEWbladAMQFzfVTWrXSfzNC/s320/codebg.gif); background: #f0f0f0; border: 1px dashed rgb(204, 204, 204); color: black; font-family: arial; font-size: 12px; height: auto; line-height: 20px; overflow: auto; padding: 0px; text-align: left; width: 99%;"&gt;&lt;code style="color: black; word-wrap: normal;"&gt;
  add pull /data/anr/traces.txt
  &lt;/code&gt;&lt;/pre&gt;
  &lt;div&gt;it will download the files to the current directory.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Another methods to find ANRs are like using popular libraries like leakCanary.&lt;/div&gt;&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;Happy exploring!!!&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>WorkManager in Android</title><link>http://smartandroidians.blogspot.com/2020/02/workmanager-in-android.html</link><category>Android</category><category>contraints</category><category>Work manager</category><author>noreply@blogger.com (androidians)</author><pubDate>Tue, 7 Sep 2021 16:56:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-5536096451965920390</guid><description>&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;
WorkManager came to android as part of JetPack library. We can use it for long running background tasks. By default all the code inside the doWork() will run in a separate thread.&amp;nbsp;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;There are two types of Workers are there.&amp;nbsp;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&amp;nbsp;1) oneTimeWorker &amp;amp;&amp;nbsp;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&amp;nbsp;2) periodicWorker.&amp;nbsp;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;One TimeWorker from the name itself, we can understand that, it will run once only. We can specify the time, add constraints etc.&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Periodic worker, similar like alarm managers but work manager respect doze mode.&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;&lt;br /&gt;&lt;/div&gt;&lt;div dir="ltr" style="text-align: left;" trbidi="on"&gt;Happy coding!!&lt;/div&gt;
&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item><item><title>Github Personal Access Token (PAT)</title><link>http://smartandroidians.blogspot.com/2021/09/github-personal-access-token-pat.html</link><author>noreply@blogger.com (androidians)</author><pubDate>Thu, 2 Sep 2021 17:07:00 +0530</pubDate><guid isPermaLink="false">tag:blogger.com,1999:blog-5182651788094880320.post-3534178470249824680</guid><description>&lt;p&gt;Github removed the support for password authentication and now we have to use the personal access token for authentication. We can set the expiration time for the token as 30 days, 60 days, 90 days etc. Once PAT is generated we have to save it securely.&amp;nbsp;&lt;/p&gt;&lt;p&gt;The steps are,&lt;/p&gt;&lt;p&gt;1. In the right hand side click on the profile icon.&lt;/p&gt;&lt;p&gt;2. From the drop down select "settings"&lt;/p&gt;&lt;p&gt;3. In settings page select developer settings from the left pane.&lt;/p&gt;&lt;p&gt;4. Now in developer settings page, click on "generate token" and check relevant permissions required&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;p&gt;Happy Exploring !!&lt;/p&gt;&lt;p&gt;&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;SmartAndroidians&lt;/div&gt;</description><thr:total xmlns:thr="http://purl.org/syndication/thread/1.0">0</thr:total></item></channel></rss>