<?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 Jansel Valentin on Medium]]></title>
        <description><![CDATA[Stories by Jansel Valentin on Medium]]></description>
        <link>https://medium.com/@jansv.ra?source=rss-0f9a1fc2187a------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*N470vxa_FlIE-Z9eMuJ6ow.jpeg</url>
            <title>Stories by Jansel Valentin on Medium</title>
            <link>https://medium.com/@jansv.ra?source=rss-0f9a1fc2187a------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Fri, 13 Nov 2026 21:25:05 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@jansv.ra/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[Jetpack Compose Previews: Delving Deep Into Their Inner Workings]]></title>
            <link>https://proandroiddev.com/jetpack-compose-previews-delving-deep-into-their-inner-workings-57601ea32ebc?source=rss-0f9a1fc2187a------2</link>
            <guid isPermaLink="false">https://medium.com/p/57601ea32ebc</guid>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[jetpack-compose]]></category>
            <category><![CDATA[androiddev]]></category>
            <category><![CDATA[android-app-development]]></category>
            <category><![CDATA[android]]></category>
            <dc:creator><![CDATA[Jansel Valentin]]></dc:creator>
            <pubDate>Tue, 30 Jul 2024 16:57:48 GMT</pubDate>
            <atom:updated>2024-07-30T16:57:48.188Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="Jetpack Compose Previews" src="https://cdn-images-1.medium.com/max/1024/1*s7o-pKHXeo3bUUZgjVf_wg.jpeg" /><figcaption>Jetpack Compose Previews</figcaption></figure><p><a href="https://developer.android.com/develop/ui/compose/tooling/previews">Jetpack Compose Previews</a> are a great tool for rapidly iterating over UI design. Understanding their underlying mechanics is optional for everyday tasks but invaluable when facing uncommon problems.</p><p>In this article, we’ll explore how Jetpack Compose Previews work. We’ll also cover how to run them using adb, the nuances in a multi-modular project, things we can customize, and even how we can preview a non @Preview annotated composable.</p><p>At the end of this article, you’ll find a complete sample project and a few practical use cases where this knowledge proves useful.</p><h3>Composable preview modes</h3><p>We’ll categorize preview inspection into two modes:</p><ul><li><strong>Design </strong>inspection through the Android Studio design window.</li><li><strong>Runtime </strong>inspection<strong> </strong>by running previews into an emulator or device.</li></ul><blockquote><strong>Note</strong>: This article focuses exclusively on <strong>runtime inspection </strong>mode<strong>.</strong></blockquote><h3>How do runtime previews work?</h3><p>The core component and heart of runtime previews is the androidx.compose.ui.tooling.PreviewActivity. This activity is responsible for hosting the composable annotated with the @Preview annotation, passing its @PreviewParameter data, and setting up the preview appearance according to the properties specified in the @Preview annotation.</p><p>PreviewActivity lives in the androidx.compose.ui:ui-tooling artifact, which we usually add as the Gradle dependencies:</p><pre>implementation(&quot;androidx.compose.ui:ui-tooling-preview&quot;)<br>debugImplementation(&quot;androidx.compose.ui:ui-tooling&quot;)</pre><p>These contain the preview annotations and the required classes that support the Jetpack Compose Preview tooling.</p><h4><strong>Runtime previews are instrumented APKs</strong></h4><p>Whenever we run a preview through the run button on the left, it triggers a compilation for the androidTest source set of the module where the preview is defined:</p><figure><img alt=":module — run preview" src="https://cdn-images-1.medium.com/max/860/1*Je3-cmG0nB4ZolKzffWUJA.png" /><figcaption>:module — run preview</figcaption></figure><p>You can verify this by checking the executed Gradle tasks in the build output tab of Android Studio:</p><figure><img alt=":module:assembleDebugAndroidTest" src="https://cdn-images-1.medium.com/max/1024/1*p2W_KRNLorsGURIffhRuZQ.png" /><figcaption>:module:assembleDebugAndroidTest</figcaption></figure><p>The output of the :assembleDebugAndroidTest task is an APK generated for instrumentation. Anecdotally, this APK is used to run the preview; however, there is an exception when the preview is run through the :app module, in which case the APK won’t be used. Since this exception does not significantly impact the overall process, we will assume that the APK is used consistently.</p><p>Lastly, Android Studio will install the instrumented APK and launch the PreviewActivity, passing information about which composable preview to render.</p><pre>Launching ModuleComposablePreview on &#39;Pixel 7 API 34&#39;.<br>Starting: Intent { <br>  act=android.intent.action.MAIN <br>  cat=[android.intent.category.LAUNCHER] <br>  cmp=me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity (has extras) <br>}<br><br>Open logcat panel for emulator Pixel 7 API 34<br>Connected to process 7329 on device &#39;Pixel_7_API_34 [emulator-5554]&#39;.</pre><h4><strong>Inspecting the AndroidManifest.xml of the preview</strong></h4><p>Running the ModuleComposablePreview results in the generation of the APK module/build/intermediates/apk/androidTest/debug/module-debug-androidTest.apk.</p><blockquote>The ModuleComposablePreview is located in the :module of the sample project.</blockquote><p>By inspecting the AndroidManifest.xml of this APK, we can uncover the following details:</p><ul><li>The instrumentation tag is present. Even though previews are compiled into an instrumented APK, they run as normal applications, meaning that any instrumentation customization won’t be used:</li></ul><pre>&lt;instrumentation<br>   android:label=&quot;Tests for me.jansv.previewinspect.module.test&quot;<br>   android:name=&quot;androidx.test.runner.AndroidJUnitRunner&quot;<br>   android:targetPackage=&quot;me.jansv.previewinspect.module.test&quot;<br>   android:handleProfiling=&quot;false&quot;<br>   android:functionalTest=&quot;false&quot; /&gt;</pre><ul><li>By placing an AndroidManifest.xml under the module/src/androidTest source set, you can customize certain aspects of the preview, such as the Application instance or the app&#39;s label:</li></ul><pre>&lt;application<br>  android:label=&quot;Module Label&quot;<br>  android:name=&quot;me.jansv.previewinspect.module.ModuleInstrumentedApplication&quot;<br>  android:debuggable=&quot;true&quot;<br>  android:extractNativeLibs=&quot;false&quot;&gt;<br>  ...<br>&lt;/application&gt;</pre><ul><li>The APK does not include an activity with the android.intent.action.MAIN intent action, meaning it won&#39;t appear on the device&#39;s home screen.</li></ul><p>These insightful hints suggest we can perform a preview run entirely manually, so let’s find out how!</p><h3>Running previews without Android Studio</h3><p>Using the adb command-line tool, you can run a composable preview without Android Studio.</p><p><em>The following steps use the </em><a href="https://github.com/janselv/JetpackPreviewInspectSample"><em>sample project</em></a><em> as a playground</em></p><h4><strong>Step 1: Generate the preview instrumented APK</strong></h4><pre>./gradlew :module:assembleDebugAndroidTest</pre><p>Once complete, the task will generate the APK at: module/build/outputs/apk/androidTest/debug/module-debug-androidTest.apk, which we will install as the next step.</p><blockquote>Note: This APK is located in a different path than the one generated by Android Studio.</blockquote><h4><strong>Step 2: Install the APK in a device or emulator.</strong></h4><pre>adb install -r -t \<br>  module/build/outputs/apk/androidTest/debug/module-debug-androidTest.apk</pre><p>Since this APK does not have an activity with the android.intent.action.MAIN intent action, it will not appear on the device&#39;s home screen.</p><h4><strong>Step 3: Launch the preview</strong></h4><p>Until now, we haven’t discussed how PreviewActivity determines which Jetpack Compose preview to render. The activity requires two intent extra parameters to specify the composable to preview:</p><ul><li><strong>composable</strong>: A fully qualified path to the @Preview composable function to render.</li><li><strong>parameterProviderClassName: </strong>A fully qualified class name of the PreviewParameterProvider subclass used to populate the preview parameter annotated with @PreviewParameter.</li></ul><p>PreviewActivity uses these intent extras to bring these entities to life through reflection.</p><p>The preview we want to launch is defined as follows:</p><pre>// ModuleComposable.kt<br><br>package me.jansv.previewinspect.module<br><br>@Composable<br>fun ModuleComposable(text: String? = null) { ... }<br><br>@Preview<br>@Composable<br>fun ModuleComposablePreview() = ModuleComposable()<br><br>@Preview<br>@Composable<br>fun ModuleComposablePreview(<br>  @PreviewParameter(TextParameterProvider::class) text: String,   <br>) = ModuleComposable(text = text)<br><br>class TextParameterProvider : PreviewParameterProvider&lt;String&gt; {<br>    override val values = (1..5).map { &quot;Param #$it&quot; }.asSequence()<br>}</pre><p>The fully qualified path for the ModuleComposablePreview is: me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposablePreview.</p><p>With this, we can finally launch the preview:</p><pre>adb shell am start \<br> -n me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity \<br> -e composable &quot;me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposablePreview&quot;</pre><figure><img alt="Jetpack Compose @Preview started with adb" src="https://cdn-images-1.medium.com/max/259/1*SDkIm8shgJJgGNbs9YpIYA.png" /><figcaption>Jetpack Compose @Preview started with adb</figcaption></figure><p>With this command, we have started the PreviewActivity as a component of the installed APK. The package name of this APK is me.jansv.previewinspect.module.test which combines the module’s namespace with the .test suffix typically added to instrumented APKs.</p><p><strong>This is good 🎉 but let’s push it a bit more…</strong></p><p>Additionally, you can run the ModuleComposablePreview with a parameter provider class passing in the parameterProviderClassName:</p><pre>adb shell am start \<br> -n me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity \<br> -e composable &quot;me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposablePreview&quot; \<br> -e parameterProviderClassName &quot;me.jansv.previewinspect.module.TextParameterProvider&quot;</pre><p><strong>And yet a bit more…</strong></p><h4>How about non @Preview composables?</h4><p>It turns out you can leverage this method to preview a composable that is not annotated with @Preview. For example, you can use the same command to preview ModuleComposable.</p><pre>adb shell am start \<br> -n me.jansv.previewinspect.module.test/androidx.compose.ui.tooling.PreviewActivity \<br> -e composable &quot;me.jansv.previewinspect.module.ModuleComposableKt.ModuleComposable&quot;</pre><h3>Previews in a multi-module setup</h3><p>Some considerations to keep in mind regarding the differences between running previews in the :app module versus a separate module are:</p><ul><li>The instrumented APK is relevant only for modules that don’t produce an APK, such as those applying the Android library plugin. Since this is not the case for the :app module, the default APK is used instead.</li><li>Because the instrumented APK generated for the :app module is not used for previews, no customizations made in the app/src/androidTest source set will be applied.</li><li>Previews for library modules can be customized through &lt;module&gt;/src/androidTest. For example, you can modify the Application instance, the PreviewActivity theme, the label, and more.</li><li>If the module you run the preview from introduces AndroidManifest.xml placeholders that are only provided through the :app module, the module will need to supply them for the instrumented APK as well for the compilation to work. You can leverage Gradle to supply them conditionally:</li></ul><pre>// module&#39;s build.gradle.kts<br><br>androidComponents {<br>    onVariants(selector().withBuildType(&quot;debug&quot;)) {<br>        it.androidTest?.manifestPlaceholders?.put(&quot;placeholder&quot;, &quot;value&quot;)<br>    }<br>}</pre><h3>Conclusion</h3><p>Jetpack Compose Previews are powerful for inspecting and refining your composables. This deep dive covered running previews with adb, understanding their compilation, and previewing composables without the @Preview annotation.</p><p>Insights into handling multi-module setups and customizations provide a comprehensive understanding to maximize their utility. With this knowledge, you can streamline your development process and tackle complex preview scenarios confidently.</p><p><em>Thanks for reading and I hope you found this guide useful 🙌🏼</em></p><p><strong>Sample app</strong></p><p><a href="https://github.com/janselv/JetpackPreviewInspectSample">GitHub - janselv/JetpackPreviewInspectSample</a></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=57601ea32ebc" width="1" height="1" alt=""><hr><p><a href="https://proandroiddev.com/jetpack-compose-previews-delving-deep-into-their-inner-workings-57601ea32ebc">Jetpack Compose Previews: Delving Deep Into Their Inner Workings</a> was originally published in <a href="https://proandroiddev.com">ProAndroidDev</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Android Test Fixtures in AGP 8.5+: A Quick Guide]]></title>
            <link>https://medium.com/@jansv.ra/android-test-fixtures-a-quick-guide-8f54d9af2afb?source=rss-0f9a1fc2187a------2</link>
            <guid isPermaLink="false">https://medium.com/p/8f54d9af2afb</guid>
            <category><![CDATA[android-development]]></category>
            <category><![CDATA[kotlin]]></category>
            <category><![CDATA[android-gradle-plugin]]></category>
            <category><![CDATA[gradle]]></category>
            <category><![CDATA[test]]></category>
            <dc:creator><![CDATA[Jansel Valentin]]></dc:creator>
            <pubDate>Tue, 09 Jul 2024 19:09:15 GMT</pubDate>
            <atom:updated>2024-07-28T22:23:36.041Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*1aU7m1ixjSFTk4VL5H-RBg.jpeg" /><figcaption>Android Test Fixtures</figcaption></figure><p>The <a href="https://issuetracker.google.com/issues/259523353#comment18">long-awaited</a> test fixture support in the Android Gradle Plugin has finally arrived in the most recent AGP 8.5 release.</p><p>Test fixtures have been <a href="https://docs.gradle.org/current/userguide/java_testing.html">available</a> for Gradle JVM projects for a while. Still, they were incompatible and unsupported by the Android Gradle Plugin since the required Gradle outgoing configuration failed to be published properly in Android projects.</p><h4><strong>Wait no more!!</strong></h4><p><em>Android test fixtures require AGP </em><strong><em>8.5.0</em></strong><em> and Kotlin </em><strong><em>1.9.20+</em></strong></p><p>With that said, let’s explore how you can expose test fixtures from a library module to any other one interested in consuming the shared test logic.</p><h4>1. Opt-in to the feature</h4><p>The feature is still experimental, hence it requires explicit opt-in.</p><p><strong><em>gradle.properties</em></strong></p><pre>android.experimental.enableTestFixturesKotlinSupport=true</pre><h4>2. Expose library test fixtures</h4><p>Enable test fixture in the library module.</p><p><strong><em>build.gradle.kts</em></strong></p><pre>android {<br>    testFixtures {<br>        enable = true<br>    }<br>}</pre><p>If you are using the Android new variant API, the following could do as well</p><pre>androidComponents {<br>    finalizeDsl {<br>        it.testFixtures {<br>            enable = true<br>        }<br>    }<br>}</pre><p>This is enough for it to expose the test fixtures to consumers. Enabling this option enabled as well a new testFixture source set in the library module. This source set is where the shared test code should reside.</p><p>Let’s suppose your library exposes an interface such as</p><pre>data class Customer(val id: String)<br><br>interface CustomerService {<br>    fun getCustomer(id: String): Customer<br>}</pre><p>and that you want to expose a fake for this service as a fixture to downstream test consumers, it suffices to place it under the testFixtures source set such as</p><pre>class FakeCustomerService : CustomerService {<br>    override fun getCustomer(id: String): Customer = Customer(id)<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/750/1*WqFp7XS3H4LX4BToox2uwA.png" /></figure><p><strong>Almost there!!</strong></p><h4>3. Using our test fixtures</h4><p>To consume FakeCustomerServicein any other module, you just need to add a textFixtures dependency in the consumer module</p><pre>testImplementation(testFixtures(project(&quot;:mylib&quot;)))</pre><blockquote>Test fixtures are directly available in the module they are exposed from so no extra ceremony is required for them.</blockquote><p><strong>And that’s it!!</strong></p><p><em>Thanks for reading!</em></p><p><em>If you found this article helpful, please consider giving it claps 👏🏻 and follow for more useful snapshots.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=8f54d9af2afb" width="1" height="1" alt="">]]></content:encoded>
        </item>
    </channel>
</rss>