<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Tonnie on Medium]]></title>
        <description><![CDATA[Stories by Tonnie on Medium]]></description>
        <link>https://medium.com/@vontonnie?source=rss-b1cf0d13a87a------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Ns6BoN4ZSMZMq9vbEPZaBg.png</url>
            <title>Stories by Tonnie on Medium</title>
            <link>https://medium.com/@vontonnie?source=rss-b1cf0d13a87a------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 24 May 2026 02:28:16 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@vontonnie/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Context-in-ViewModel Debate]]></title>
            <link>https://medium.com/@vontonnie/context-in-viewmodel-debate-239485108cfb?source=rss-b1cf0d13a87a------2</link>
            <guid isPermaLink="false">https://medium.com/p/239485108cfb</guid>
            <category><![CDATA[context]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[viewmodel]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Tonnie]]></dc:creator>
            <pubDate>Wed, 05 Nov 2025 10:46:36 GMT</pubDate>
            <atom:updated>2025-11-05T10:46:36.633Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*rkNDItlhxNYxqfdz61uucw.png" /></figure><p>The <em>Context-in-ViewModel</em> debate has been echoing through Android circles for years — a quiet civil war between <em>practicality</em> and <em>purity</em>.</p><p>The confusion stems from how Android’s lifecycle works — not all contexts are created equal, and misusing one can quietly wreak havoc.</p><p>Here are 4 Commandments of Context in Android ViewModels - guiding principles to help you write clean, leak-free, and lifecycle-safe code.</p><h4>Thou shalt not store Context in a ViewModel property.</h4><p>Never do this:</p><pre>class MyViewModel(val context: Context) : ViewModel() // 🚫 BAD</pre><p>This pins your Activity to memory and prevents proper Garbage Collection after configuration changes. If you must access context frequently, use:</p><pre>class MyViewModel(application: Application) : AndroidViewModel(application)</pre><p>Then retrieve context via:</p><pre>val appContext = getApplication&lt;Application&gt;().applicationContext</pre><p>This returns the <strong>Application context</strong>, not the UI one.</p><p>You can also inject the context in a ViewModel:</p><pre>@HiltViewModel<br>class MyViewModel @Inject constructor(<br>    @ApplicationContext private val context: Context<br>) : ViewModel() {</pre><p>If you must inject Context into a ViewModel using Hilt, always qualify it with @ApplicationContext — anything else risks tethering your ViewModel to a destroyed Activity.</p><h4>Thou shalt only use Context for short-lived operations</h4><p>While the general rule is to avoid using Context in a regular ViewModel, there are valid cases for short-lived Ops where you temporarily need it.</p><p><em>Accessing a String Resource - useful for localized messages or logging</em>:</p><pre>fun getWelcomeMessage(context: Context): String {<br>    return context.getString(R.string.welcome_message)<br>}</pre><p><em>Getting a System Service - like ClipboardManager, Vibrator etc.</em></p><pre>fun copyTextToClipboard(context: Context, text: String) {<br>    val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager<br>    clipboard.setPrimaryClip(ClipData.newPlainText(&quot;Copied&quot;, text))<br>}</pre><p>This is <strong>transient</strong> usage — no storage, no reference kept, no leak risk.</p><h4>Thou shalt not pass an Activity or a View Context</h4><p>When you pass a Context tied to a Fragment or Activity, it references their UI lifecycles.</p><p>If your ViewModel holds it longer than the <em>screen’s </em>lifetime, the system can’t garbage-collect the destroyed Activity, resulting in a leak.</p><pre>// 🚫 BAD: Activity context passed directly to the ViewModel<br>class MainActivity : ComponentActivity() {<br><br>    private val viewModel = MyViewModel(this) // ❌ leaks Activity after rotation<br><br>    override fun onCreate(savedInstanceState: Bundle?) {<br>        viewModel.startSomething()<br>    }<br>}<br><br>class MyViewModel(private val context: Context) : ViewModel() {<br><br>    fun startSomething() {<br>        // Holding Activity context inside ViewModel = potential memory leak<br>        val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator<br>        vibrator.vibrate(VibrationEffect.createOneShot(100, 255))<br>    }<br>}</pre><p><strong>Why this is bad:</strong></p><ul><li>The context here references the Activity, not the application</li><li>When the screen rotates, Activity dies, but ViewModel may live longer</li><li>That lingering reference keeps the destroyed Activity in memory which is what we call a memory leak</li></ul><p>Think of it like borrowing a USB stick but forgetting to give it back — now you’re holding onto the whole desktop!</p><p>However, in the <em>Compose world</em>, this pattern is safe because it is short-lived and you are not saving the context anywhere:</p><pre>val context = LocalContext.current<br>viewModel.analyzeGalleryImage(context, imageUri)</pre><h4>If it smells like a UI operation, move it out of the ViewModel</h4><p>If you ever find yourself doing something like this, inside the ViewModel — 🚨 that isi code a smell.</p><pre>Toast.makeText(context, &quot;Saved!&quot;, Toast.LENGTH_SHORT).show()</pre><p>Instead, send UiAction event and let the Activity or Composable handle it.</p><h4>Wrapping Up the Debate</h4><p>Context is not forbidden inside ViewModels but it should be treated like radioactive material: useful in tiny, controlled doses.</p><p>You can safely use it for quick, one-off tasks like reading resources, grabbing a system service, or decoding a file — but never hold onto it.</p><p>When in doubt, prefer the <strong>Application context</strong> or push the call down into a <strong>repository</strong> or <strong>use case</strong> layer.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=239485108cfb" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[From Silence to Sound: Fetching Android Ringtones]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://levelup.gitconnected.com/from-silence-to-sound-fetching-android-ringtones-0de0f5e11830?source=rss-b1cf0d13a87a------2"><img src="https://cdn-images-1.medium.com/max/890/1*OhIt8C_sN0oacsWKC3qzjQ.jpeg" width="890"></a></p><p class="medium-feed-snippet">From Silence to Sound: Fetching Android Ringtones</p><p class="medium-feed-link"><a href="https://levelup.gitconnected.com/from-silence-to-sound-fetching-android-ringtones-0de0f5e11830?source=rss-b1cf0d13a87a------2">Continue reading on Level Up Coding »</a></p></div>]]></description>
            <link>https://levelup.gitconnected.com/from-silence-to-sound-fetching-android-ringtones-0de0f5e11830?source=rss-b1cf0d13a87a------2</link>
            <guid isPermaLink="false">https://medium.com/p/0de0f5e11830</guid>
            <category><![CDATA[android]]></category>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[app-development]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[Tonnie]]></dc:creator>
            <pubDate>Mon, 28 Apr 2025 14:40:55 GMT</pubDate>
            <atom:updated>2025-07-13T13:59:27.366Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Null? No Problem, Introducing String?. orEmpty()]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/admiring-multipotentiality/null-no-problem-introducing-string-orempty-e41d22c9b689?source=rss-b1cf0d13a87a------2"><img src="https://cdn-images-1.medium.com/max/1024/1*bVNL7uG4x20heTqHi4KtTA.png" width="1024"></a></p><p class="medium-feed-snippet">Kotlin&#x2019;s way of handling nulls is one of its defining trademark features, enabling developers write safer more concise code.</p><p class="medium-feed-link"><a href="https://medium.com/admiring-multipotentiality/null-no-problem-introducing-string-orempty-e41d22c9b689?source=rss-b1cf0d13a87a------2">Continue reading on Admiring Multipotentiality »</a></p></div>]]></description>
            <link>https://medium.com/admiring-multipotentiality/null-no-problem-introducing-string-orempty-e41d22c9b689?source=rss-b1cf0d13a87a------2</link>
            <guid isPermaLink="false">https://medium.com/p/e41d22c9b689</guid>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[admiringmultipotentiality]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Tonnie]]></dc:creator>
            <pubDate>Mon, 04 Nov 2024 20:47:56 GMT</pubDate>
            <atom:updated>2024-11-04T20:47:56.991Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[From Millions to Billions: Compact Number Formatting]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@vontonnie/from-millions-to-billions-compact-number-formatting-b0d68addd0af?source=rss-b1cf0d13a87a------2"><img src="https://cdn-images-1.medium.com/max/650/1*kPTWptZqfecRna1J5sj7eg.png" width="650"></a></p><p class="medium-feed-snippet">Large numbers are everywhere; for example, the age of the universe is estimated to be around 13.7 billion years.</p><p class="medium-feed-link"><a href="https://medium.com/@vontonnie/from-millions-to-billions-compact-number-formatting-b0d68addd0af?source=rss-b1cf0d13a87a------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@vontonnie/from-millions-to-billions-compact-number-formatting-b0d68addd0af?source=rss-b1cf0d13a87a------2</link>
            <guid isPermaLink="false">https://medium.com/p/b0d68addd0af</guid>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[algorithms]]></category>
            <category><![CDATA[numbers]]></category>
            <category><![CDATA[java]]></category>
            <dc:creator><![CDATA[Tonnie]]></dc:creator>
            <pubDate>Sun, 20 Oct 2024 11:00:06 GMT</pubDate>
            <atom:updated>2024-10-20T11:00:06.012Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[VIM at Your Fingertips]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@vontonnie/vim-at-your-fingertips-a2adc4f92c59?source=rss-b1cf0d13a87a------2"><img src="https://cdn-images-1.medium.com/max/1024/1*ABtrbpAyj4Y6DK2fPfaKrQ.png" width="1024"></a></p><p class="medium-feed-snippet">Vim stands out as one of the most powerful tools for navigating and editing code with speed and precision.</p><p class="medium-feed-link"><a href="https://medium.com/@vontonnie/vim-at-your-fingertips-a2adc4f92c59?source=rss-b1cf0d13a87a------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@vontonnie/vim-at-your-fingertips-a2adc4f92c59?source=rss-b1cf0d13a87a------2</link>
            <guid isPermaLink="false">https://medium.com/p/a2adc4f92c59</guid>
            <category><![CDATA[ideavim]]></category>
            <category><![CDATA[plugins]]></category>
            <category><![CDATA[vim]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[intellij]]></category>
            <dc:creator><![CDATA[Tonnie]]></dc:creator>
            <pubDate>Sun, 13 Oct 2024 16:48:10 GMT</pubDate>
            <atom:updated>2024-11-26T16:19:32.190Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Cleaner Kotlin Code with Ktlint Gradle]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@vontonnie/cleaner-kotlin-code-with-ktlint-gradle-4cd34e691e92?source=rss-b1cf0d13a87a------2"><img src="https://cdn-images-1.medium.com/max/1024/1*eXdtpXbcS1DJ3lsFRckZSQ.jpeg" width="1024"></a></p><p class="medium-feed-snippet">A Linter is a tool used to analyze source code and flag potential errors and stylistic inconsistencies that could affect code quality.</p><p class="medium-feed-link"><a href="https://medium.com/@vontonnie/cleaner-kotlin-code-with-ktlint-gradle-4cd34e691e92?source=rss-b1cf0d13a87a------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@vontonnie/cleaner-kotlin-code-with-ktlint-gradle-4cd34e691e92?source=rss-b1cf0d13a87a------2</link>
            <guid isPermaLink="false">https://medium.com/p/4cd34e691e92</guid>
            <category><![CDATA[linting]]></category>
            <category><![CDATA[linter]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[lint]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Tonnie]]></dc:creator>
            <pubDate>Tue, 27 Aug 2024 05:55:53 GMT</pubDate>
            <atom:updated>2024-08-27T05:55:53.525Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[Connecting Room Tables Using Foreign Keys]]></title>
            <link>https://medium.com/@vontonnie/connecting-room-tables-using-foreign-keys-c19450361603?source=rss-b1cf0d13a87a------2</link>
            <guid isPermaLink="false">https://medium.com/p/c19450361603</guid>
            <category><![CDATA[database]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Tonnie]]></dc:creator>
            <pubDate>Wed, 01 May 2024 12:11:57 GMT</pubDate>
            <atom:updated>2024-06-04T05:45:44.721Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/552/1*c6FadKo82fW6rt0aQQdyrQ.png" /></figure><p>While Room simplifies many aspects of database handling, understanding foreign keys is crucial for creating relational databases with complex relationships between entities.</p><p>A foreign key is a referenced column or columns that can be found in a different table.</p><p>A foreign key establishes a link between two tables in a database to create a way of cross-referencing the two tables.</p><h4>The Idea behind Foreign Key</h4><p>The idea behind foreign keys is based on a <em>parent-child relationship</em> where we have a parent table and a child table.</p><p>The below diagram maps a HR system with 2 tables - the first table stores employees’ basic info while the other contains extra employees’ details.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1000/1*E4oyK6g9YjQMS6EkhXdiLQ.png" /><figcaption>Parent and Child Tables</figcaption></figure><p>The child table is linked to its parent table through a foreign column(key) which references the parent table.</p><p>In essence, foreign keys establish a connection between related tables facilitating the retrieval of interconnected data within the database.</p><p>A table can have multiple foreign keys that reference different tables.</p><h4>What about Primary Key</h4><p>A primary key is a <em>unique identifier</em> for each row in a database table.</p><p>It <em>prevents duplicated rows</em> by ensuring no records share the same key while also guaranteeing that primary column <em>doesn’t contain null </em>values.</p><p>Room database enables you to autogenerate Primary key by using an incrementing integer value, a UUID (Universally Unique Identifier), or a custom algorithm.</p><h4>Primary Key vs Foreign Key</h4><p>While a primary key uniquely identifies records within its own table, a foreign key acts as a <em>bridge</em>, connecting records across multiple tables.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/717/1*qGNEkIBOlJM0WJDMAxPPZg.png" /><figcaption>Primary Key vs Foreign Key</figcaption></figure><p>The foreign key column (referenced column on child table) must contain only unique values and often it is the primary key of the parent table.</p><h4>Anatomy of Foreign Key in Code</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/665/1*R6QkGugyF_G2V7OdEgLyiw.png" /><figcaption>Parent Table</figcaption></figure><p>The code for the above parent table looks like this:</p><pre>@Entity(tableName = &quot;customers_table&quot;)<br>data class Customer(<br>    @PrimaryKey(autoGenerate = true)<br>    @ColumnInfo(name = &quot;customer_id&quot;)<br>    val customerId: Long,<br>    @ColumnInfo(name = &quot;name&quot;)<br>    val name: String,<br>    @ColumnInfo(name = &quot;phone_number&quot;)<br>    val phoneNumber: String,<br>    @ColumnInfo(name = &quot;address&quot;)<br>    val address: String<br>)</pre><ul><li>@Entity marks the Customer class as an entity in the Room database</li><li>@PrimaryKey annotation marks the customerId property as the primary key for the Customer entity.</li><li>autoGenerate = true parameter specifies that Room should automatically generate unique values when inserting new records.</li><li>@ColumnInfo annotation specifies column details and provides a flexibile way to customize the <em>column name</em>, <em>data type</em> etc</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/665/1*c5Z3jbWizT1TSQEFuvt5Cw.png" /><figcaption>Child Table</figcaption></figure><p>The code for the above child table looks like this:</p><pre>@Entity(<br>        tableName = &quot;orders_table&quot;,<br>        foreignKeys = [<br>            ForeignKey(<br>                    entity = Customer::class,<br>                    parentColumns = arrayOf(&quot;customer_id&quot;),<br>                    childColumns = arrayOf(&quot;customer_id&quot;),<br>                    onUpdate = ForeignKey.CASCADE,<br>                    onDelete = ForeignKey.CASCADE<br>            )]<br>)<br>data class Order(<br>    @PrimaryKey(autoGenerate = true)<br>    @ColumnInfo(name = &quot;order_id&quot;)<br>    val orderId: Long,<br>    @ColumnInfo(name = &quot;customer_id&quot;)<br>    val customerId: Long,<br>    @ColumnInfo(name = &quot;item_id&quot;)<br>    val itemId: String,<br>    @ColumnInfo(name = &quot;order_details&quot;)<br>    val orderDetails: String<br>)</pre><ul><li>Order entity class is annotated with <a href="https://twitter.com/entity">@Entity</a> and includes the foreignKeys parameter</li><li>Inside the foreign keys parameter we define an array of ForeignKey objects</li><li>entity = Customer::classspecifies the entity (table) that the foreign key references, in this case it is the parent Customer entity</li><li>parentColumns = arrayOf(&quot;customer_id&quot;) specifies the column in the referenced parent Customer entity that the foreign key corresponds to i.e. the customer_id column in the Customer entity.</li><li>childColumns = arrayOf(&quot;customer_id&quot;) specifies the column in the Order child entity that the foreign key is defined on</li></ul><h3>Handling updates and deletions</h3><p>We can optionally use onDelete and onUpdate attributes to define the behavior when a record on the parent is deleted or updated.</p><p>ForeignKey.CASCADE means when a row in the parent table is deleted, the child table also deletes associated rows on its table.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/665/1*Jmw03-Vm4okzLgK2qlDMtw.gif" /></figure><h4>Relationships</h4><p>Foreign keys create relationships between tables, defining how data in one table relates to data in another.</p><p>These relationships can be one-to-one, one-to-many, or many-to-many as detailed on this blog post.</p><p><a href="https://medium.com/@vontonnie/exploring-one-to-one-relationship-models-in-room-database-0e74f6832707">Exploring One-to-One Relationship Models in Room Database</a></p><h4>Conclusion</h4><p>Together, primary and foreign keys form the foundation of relational databases, enabling developers to design robust and scalable data models.</p><p>Cheers guys!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c19450361603" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Exploring One-to-One Relationship Models in Room Database]]></title>
            <link>https://medium.com/@vontonnie/exploring-one-to-one-relationship-models-in-room-database-0e74f6832707?source=rss-b1cf0d13a87a------2</link>
            <guid isPermaLink="false">https://medium.com/p/0e74f6832707</guid>
            <category><![CDATA[android]]></category>
            <category><![CDATA[database]]></category>
            <dc:creator><![CDATA[Tonnie]]></dc:creator>
            <pubDate>Tue, 30 Apr 2024 03:55:46 GMT</pubDate>
            <atom:updated>2026-01-03T07:27:46.742Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*kLBA1Cr1ZUmhLw5OcNhJUQ.gif" /></figure><p>Room Database is a persistence library provided by Google and acts as an abstraction layer over the underlying SQLite database.</p><p>This means Room simplifies database setup and configuration allowing your app to interact with the database through ordinary function calls.</p><p>One fundamental concept in database design that Room Database handles exceptionally well is the one-to-one relationship.</p><p>This relationship type defines a scenario where <em>each row in one table is associated with exactly one row</em> in another table.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/758/1*xil9VQME1YRzbaasmuAfYA.png" /><figcaption>One-to-One tables relation</figcaption></figure><p>On the above illustration, each row from table one is mapped to one row of table two. This concept <em>can be visualized as a single table split halfway with a common property</em>.</p><h4>Classic Examples</h4><p>Some examples of one-to-one relationship modeling include:</p><ul><li><em>Country and Capital: </em>each country has a unique capital, and each capital is associated with exactly one country</li><li><em>User and UserProfile</em>: user’s username and password data can be stored in one table while additional profile information (like email, bio, etc.) could be stored in a separate table linked to the user table.</li><li><em>Employee and EmployeeInfo: </em>HR database system can have employee’s basic details like Id, name, etc in one table with a supplementary table holding additional info such as contact details, home address etc.</li><li><em>Customer and CreditCard: </em>In a banking system each customer might have one credit card associated with their account.</li></ul><h4>One-to-One Relationship Model in Code</h4><p>This is how the above tables would look like in code.</p><pre>@Entity(tableName = &quot;countries_table&quot;)<br>data class Country(@PrimaryKey val id: Long, val name: String, val lang: String)<br><br>@Entity(<br>        tableName = &quot;capitals_table&quot;,<br>        foreignKeys = [<br>            ForeignKey(<br>                    entity = Country::class,<br>                    parentColumns = arrayOf(&quot;id&quot;),<br>                    childColumns = arrayOf(&quot;country_id&quot;),<br>                    onDelete = ForeignKey.CASCADE<br>            )<br>        ]<br>)<br><br>data class Capital(<br>    @PrimaryKey(autoGenerate = true)<br>    @ColumnInfo(&quot;id&quot;)<br>    val id: Long,<br>    @ColumnInfo(name = &quot;country_id&quot;)<br>    val countryId: Long,<br>    @ColumnInfo(name = &quot;name&quot;)<br>    val name: String<br>)</pre><p>You will notice that Capital entity specifies a foreign key to link it to the Country Entity.</p><p>The foreign key column will act as a common column and will be used on the POJO class to specify the relationship between Country and Capital.</p><p>You can find more info on foreign keys on this blog.</p><p><a href="https://medium.com/@vontonnie/connecting-room-tables-using-foreign-keys-c19450361603">Connecting Room Tables Using Foreign Keys</a></p><h4>Relation Wrapper for DAO</h4><p>When querying the database the dao will return neither Country nor the Capital entities.</p><p>This is where an <em>intermediary</em> Plain Old Java Object (POJO) comes in. We need a POJO helper class which models one-to-one relationship between two entities.</p><pre>data class CountryAndCapital(<br>    @Embedded val country: Country,<br>    @Relation(<br>            parentColumn = &quot;id&quot;,<br>            entityColumn = &quot;country_id&quot;<br>    )<br>    val capital: Capital<br>)</pre><p>This POJO tells Room how our entities relate and work together.</p><ul><li>The name of the POJO is CountryAndCapital which is a convention to combine first and second class names using With or And conjunctions</li><li>In the POJO one entity must include a reference to the primary key of the other entity</li><li>@Embedded annotation applied to the country property tells Room to include all properties of the Country class as columns in the CountryWithCapital table. This means that properties like id, name, and lang from the Country class will internally be treated as columns in the database table for CountryWithCapital</li><li>@Relation is an annotation used in a POJO to specify the relationship between Country and Capital and automatically fetch relation entities</li><li>parentColumn refers to the column in the parent entity - <em>Country</em></li><li>entityColumn refers to the column in the child entity - <em>Capital</em></li></ul><h4>Querying a One-to-One Relationship Model</h4><p>We can then query all users with their addresses like this:</p><pre>@Dao<br>interface CountryCapitalDao {<br>    @Query(&quot;SELECT * FROM countries&quot;)<br>    fun getCountriesWithCapitals(): List&lt;CountryWithCapital&gt;?<br>}</pre><p>This query will return a list of CountryAndCapital objects, each containing a Country object and a Capital object which you can then employ on your project’s logic.</p><p>Happy Coding</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0e74f6832707" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Marquee Modifier Dancing Effect]]></title>
            <link>https://medium.com/@vontonnie/marquee-modifier-dancing-effect-0679ccee7d4f?source=rss-b1cf0d13a87a------2</link>
            <guid isPermaLink="false">https://medium.com/p/0679ccee7d4f</guid>
            <category><![CDATA[modifier]]></category>
            <category><![CDATA[animation]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[jetpack-compose]]></category>
            <category><![CDATA[android-app-development]]></category>
            <dc:creator><![CDATA[Tonnie]]></dc:creator>
            <pubDate>Sun, 24 Mar 2024 17:22:58 GMT</pubDate>
            <atom:updated>2024-03-25T17:33:59.970Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/710/1*x2SUFehR-KJh948sOmzNug.gif" /></figure><p>Jetpack Compose continues to revolutionize the way we create beautiful UI interfaces.</p><p>One of its intriguing features is the eye-catching Marquee effect achievable with just a few simple keystrokes using the standard compose modifier.</p><p>The basic marquee modifier allows you to animate text horizontally making it scroll continuously across the screen.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*w_wQEFzTLUh3vw1fl79rdw.gif" /><figcaption>Dancing text across the screen courtesy of basic marque modifier</figcaption></figure><p>This can be useful when dealing with long chunks of text that do not fitting within the available space.</p><p>The scrolling effect not only ensures the visibility of the text but also adds a touch of flair to your app as seen on <em>news tickers</em>, <em>breaking news alerts</em>, <em>social media feeds</em>, t<em>estimonials</em>, <em>real-time currency</em> and <em>stocks updates</em>.</p><p>The marque effect code is incredibly simple and beginner-friendly:</p><pre>@OptIn(ExperimentalFoundationApi::class)<br>@Composable<br>fun MarqueeTextExample() {<br>    Text(<br>        text = &quot;Breaking News: Jetpack Compose is Amazing!&quot;,<br>        modifier = Modifier<br>            .basicMarquee()<br>            .padding(20.dp)<br>    )<br>}</pre><p>Marquee modifier is still marked as experimental in Jetpack Compose hence the @OptIn(ExperimentalFoundationApi::class) annotation.</p><p>We simply applybasicMarquee modifier to create the scrolling effect. The padding modifier adds some space around the scrolling text.</p><p>The modifier can even be customized further to control the speed, iterations, and delay, allowing you to tailor it to your specific design requirements.</p><p>For example, if you want the text to zip across the screen at speed of light, you simply need to increase its default velocity</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/600/1*61wamT-31uAAR8YycUMANg.gif" /><figcaption>Marque Modifier with increased velocity</figcaption></figure><p>The default speed is 30 dps per second but can this can be stepped up as needed.</p><pre>@OptIn(ExperimentalFoundationApi::class)<br>@Composable<br>fun MarqueeTextExample() {<br>    Text(<br>            text = &quot;Breaking News: Jetpack Compose is Amazing!&quot;,<br>            modifier = Modifier<br>                    .basicMarquee(<br>                            velocity = 500.dp,<br>                            initialDelayMillis = 2_000,<br>                            iterations = 5<br>                    )<br>                    .padding(8.dp)<br>    )<br>}</pre><p>Marquee modifier can also be applied to a non-text composables as shown on this animation composed of a Row containing Cyan-colored Boxes</p><figure><img alt="dgs" src="https://cdn-images-1.medium.com/max/600/1*llzZU7aRrYTKS081C5y6ZQ.gif" /><figcaption>Applying Marque to non-text composable</figcaption></figure><p>The boxes scroll horizontally courtesy ofbasicMarquee modifier applied on the Row.</p><pre>@OptIn(ExperimentalFoundationApi::class)<br>@Composable<br>private fun MarqueeEffect() {<br><br>    Box(modifier = Modifier.padding(top = 80.dp)) {<br><br>        //Marquee modifier applied to the row<br>        Row(modifier = Modifier.basicMarquee()) {<br><br>            //Draw row content<br>            repeat(30) {<br>                Box(<br>                        modifier = Modifier<br>                                .size(40.dp)<br>                                .padding(4.dp)<br>                                .background(Color.Cyan)<br>                                .padding(4.dp)<br>                ) {<br><br>                    Canvas(<br>                            modifier = Modifier<br>                                    .size(20.dp)<br>                                    .align(Alignment.Center)<br>                    ) {<br>                        drawCircle(color = Color.Magenta)<br><br>                    }<br>                }<br><br>                Spacer(modifier = Modifier.width(8.dp))<br>            }<br>        }<br>    }<br>}</pre><p>You can replace the boxes with any other composable to create a scrolling effect for different types of content. For instance, you could use images, icons or custom composables instead of the boxes.</p><p>It’s worth noting that scrolling occurs when the size of the content exceeds the available space. For example, when the device is rotated from portrait to landscape, the marquee effect doesn’t play.</p><p>Another hack is theanimationMode which determines if the marquee should start animating Immediately or only WhileFocused. However, for WhileFocused mode, the content must be made focusable.”</p><p>You can adjust the earlier code to request for focus:</p><pre>@OptIn(ExperimentalFoundationApi::class)<br>@Composable<br>fun MarqueeTextExample() {<br><br>    val focusRequester = remember { FocusRequester() }<br>    Text(<br>            text = &quot;Breaking News: Jetpack Compose is Amazing!&quot;,<br>            modifier = Modifier<br>                    .basicMarquee(animationMode = MarqueeAnimationMode.WhileFocused)<br>                    .focusRequester(focusRequester)<br>                    .focusable()<br>                    .clickable { focusRequester.requestFocus() }<br>                    .padding(40.dp)<br><br>    )<br>}</pre><p>Rather than allowing the marquee to run endlessly which takes more battery life, a more effective approach is to display the marquee when the user clicks or focuses on the text intending to see more content.</p><p>It is a wrap guys, keep exploring!</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=0679ccee7d4f" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Power of Logic: Tiny Functions on Boolean Class]]></title>
            <link>https://blog.stackademic.com/power-of-logic-tiny-functions-on-boolean-class-d4d7a11b6d52?source=rss-b1cf0d13a87a------2</link>
            <guid isPermaLink="false">https://medium.com/p/d4d7a11b6d52</guid>
            <category><![CDATA[boolean]]></category>
            <category><![CDATA[infix]]></category>
            <category><![CDATA[android]]></category>
            <category><![CDATA[kotlin]]></category>
            <dc:creator><![CDATA[Tonnie]]></dc:creator>
            <pubDate>Wed, 28 Feb 2024 17:02:52 GMT</pubDate>
            <atom:updated>2024-10-29T02:38:16.308Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/608/1*feddU-m1WgvZJ9lKOl67Hg.jpeg" /></figure><p>In the world of programming, Boolean logic serves as the centerpiece in the decision-making processes.</p><p>Boolean is a data type that can hold either atrue or a false value.</p><p>One of Kotlin’s standout features is support for <strong>infix</strong> functions which becomes handy in the context of Boolean functions such as and, or, xor .</p><p>Infix functions remind me of the iconic Argentine Tango dance where 2 dancers tango to perform intricate and synched dance moves without stepping on each other’s toes.</p><p>Similarly, infix notation allows us to combine two code entities in a fluid manner without the need for dots or parentheses.</p><pre>//Infix function demo<br>infix fun Int.add(other: Int): Int {<br>    return this + other<br>}<br><br>fun main() {<br>    val result = 5 add 3  // applying infix function<br>    println(result)      // prints: 8<br>}</pre><p>We can leverage the power of infix functions in logic operations.</p><p><strong>Tiny function 1 - </strong><strong>and</strong></p><p>Typically, we go for the double ampersand shorthand when both conditions need to be true.</p><pre>fun main() {<br>   <br>    val isSunny = true<br>    val isWarm = true<br>    val isSunnyAndWarm = isSunny &amp;&amp; isWarm // using ampersand<br>}</pre><p>With a little help from infix notation, you can express the same using <strong>and</strong> .</p><pre>fun main() {<br>    <br>    val isSunny = true<br>    val isWarm = true<br>    <br>    //using the and operator<br>    val isSunnyAndWarm = isSunny and isWarm // This will be true<br>}</pre><p><strong>Tiny function 2 - </strong><strong>or</strong></p><p>Tired of letting your finger traverse blindly on your keyboard looking for the below double pipe symbol?</p><pre>fun main() {<br>    <br>    val isRaining = false<br>    val isSnowing = true<br>    val isRainyOrSnowy = isRaining || isSnowing // This will return true<br>}</pre><p>Well, operator or guarantees zero strain as demonstrated on this snippet.</p><pre>fun main() {<br><br>    val isRaining = false<br>    val isSnowing = true<br>    <br>    //using or operator<br>    val isRainyOrSnowy = isRaining or isSnowing // returns true<br>}</pre><p>The or function returns true if at least one of the operands is true. It evaluates to false only if both operands are false.</p><p><strong>Tiny Function 3 - </strong><strong>xor</strong></p><p>xor operator is an infix function that performs a <em>logical XOR</em> operation.</p><p>A logical XOR (<em>exclusive OR</em>) operation is a binary operation that takes two boolean operands - the result of the operation is true if exactly one of the operands is false.</p><p>If <em>both operands are true</em> or if <em>both are false</em>, then xor evaluates to false.</p><p>Imagine a situation where there are two security measures in place: a keycard scanner and a biometric fingerprint scanner. The restricted area can only be accessed if a person presents either a valid keycard or a verified fingerprint, but not both.</p><p>If someone tries to access the area with both a valid keycard and a verified fingerprint, they should be denied entry.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fpl.kotl.in%2F2120QO4cr&amp;display_name=Kotlin+Playground&amp;url=https%3A%2F%2Fpl.kotl.in%2F2120QO4cr&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=kotl" width="800" height="300" frameborder="0" scrolling="no"><a href="https://medium.com/media/e8d1b5db3a98130b2412e85d59aa5339/href">https://medium.com/media/e8d1b5db3a98130b2412e85d59aa5339/href</a></iframe><p>However, if they either present a card or a finger then access is granted.</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fpl.kotl.in%2F7IVYNjghI&amp;display_name=Kotlin+Playground&amp;url=https%3A%2F%2Fpl.kotl.in%2F7IVYNjghI&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=kotl" width="800" height="300" frameborder="0" scrolling="no"><a href="https://medium.com/media/33dbabe9d8cc3de62182eaae979ede3d/href">https://medium.com/media/33dbabe9d8cc3de62182eaae979ede3d/href</a></iframe><p>If they don’t have the card or a fingerprint, then they are locked out.</p><p><strong>Tiny Function 4 - </strong><strong>not</strong></p><p>Though not an infix function, the operator not is yet another out-of-box tiny function provided on the Boolean class for negating Boolean values.</p><p>Typically, you would perform the negation like this:</p><pre>fun main() {<br><br>    val isSunny = true<br>    val isNotSunny = !isSunny // returns false<br><br>}</pre><p>However, you can spice things up by using the .not operator.</p><pre>fun main() {<br><br>    val isSunny = true<br><br>   // using not negation operator with if-else block<br>   if (isSunny.not()) {<br>        println(&quot;It&#39;s not sunny&quot;)<br>    } else {<br>        println(&quot;It&#39;s sunny&quot;)<br>    }<br><br>}</pre><p>There you have it - embrace the power of Boolean logic in your Kotlin projects and elevate your coding experience to new heights!</p><h3>Stackademic 🎓</h3><p>Thank you for reading until the end. Before you go:</p><ul><li>Please consider <strong>clapping</strong> and <strong>following</strong> the writer! 👏</li><li>Follow us <a href="https://twitter.com/stackademichq"><strong>X</strong></a> | <a href="https://www.linkedin.com/company/stackademic"><strong>LinkedIn</strong></a> | <a href="https://www.youtube.com/c/stackademic"><strong>YouTube</strong></a> | <a href="https://discord.gg/in-plain-english-709094664682340443"><strong>Discord</strong></a> | <a href="https://newsletter.plainenglish.io/"><strong>Newsletter</strong></a> | <a href="https://open.spotify.com/show/7qxylRWKhvZwMz2WuEoua0"><strong>Podcast</strong></a></li><li><a href="https://differ.blog/"><strong>Create a free AI-powered blog on Differ.</strong></a></li><li>More content at <a href="https://stackademic.com/"><strong>Stackademic.com</strong></a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=d4d7a11b6d52" width="1" height="1" alt=""><hr><p><a href="https://blog.stackademic.com/power-of-logic-tiny-functions-on-boolean-class-d4d7a11b6d52">Power of Logic: Tiny Functions on Boolean Class</a> was originally published in <a href="https://blog.stackademic.com">Stackademic</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>