# Gadget Habit

Update (February 25, 2026): The scroll indicator API described in this post was reverted in Compose Foundation 1.11.0-alpha06. According to the ...

This is one page of public article previews, not the complete archive. Follow Next page to continue. Summaries are not the original full articles.

## Adding Scrollbars to Jetpack Compose

DevFeed: [Adding Scrollbars to Jetpack Compose](<https://devfeed.tech/articles/adding-scrollbars-to-jetpack-compose-25121.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2026/02/11/custom-scroll-indicators-in-jetpack-compose-foundation/>)

Author: Michael Evans

Published: 2026-02-12T02:14:16Z

Content type: article

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Compose](<https://devfeed.tech/topics/compose.md>), [Jetpack Compose](<https://devfeed.tech/topics/jetpack-compose.md>), [ui](<https://devfeed.tech/topics/ui.md>), [Android](<https://devfeed.tech/topics/android.md>), [changelog](<https://devfeed.tech/topics/changelog.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [article](<https://devfeed.tech/tags/article.md>), [changelog](<https://devfeed.tech/tags/changelog.md>), [compose](<https://devfeed.tech/tags/compose.md>), [jetpack-compose](<https://devfeed.tech/tags/jetpack-compose.md>), [ui](<https://devfeed.tech/tags/ui.md>), [update](<https://devfeed.tech/tags/update.md>)

### AI overview

This article explains the custom scroll indicator APIs introduced in Jetpack Compose Foundation 1.11.0-alpha01. It describes why scrollbars improve feedback about scrollability, content length, position, and visible range, and notes that the API was later reverted in 1.11.0-alpha06 after API Council feedback. The article applies to alpha01 through alpha05, while the provided text ends before the detailed API overview.

### Source excerpt

Update (February 25, 2026): The scroll indicator API described in this post was reverted in Compose Foundation 1.11.0-alpha06. According to the changelog, the revert was because of API Council feedback, so we'll likely get a new version of the API before long. In the meantime, this article still applies to Foundation 1.11.0-alpha01 through alpha05, and the concepts might carry over when the replacement lands. Jetpack Compose Foundation 1.11-alpha-01 introduced a long-awaited feature: the ability to add custom scroll indicators to scrollable containers. If you've been following Compose development, you've probably noticed that scrollbars were conspicuously absent -- unlike Android Views, which have had them built-in for years. This has been a frequently asked question on Stack Overflow and discussed in the Android developer community, and it's been on the Compose roadmap since at least September 2024 (though the roadmap may have changed by the time you're reading this). Why scroll indicators matter Scrollbars have been around for decades and are an effective, interactive UI control. As Blake Watson points out, scrollbars provide immediate visual feedback that's hard to replicate: They indicate scrollability -- Users can instantly see that content extends beyond the visible area They show content length -- The scrollbar's size relative to the track gives a sense of how much content exists They show current position -- Users know exactly where they are in the document They show visible range -- The thumb size indicates how much of the total content is currently visible Without scroll indicators, users in Compose apps have been left guessing about scrollable content, especially in long lists or documents. The new APIs finally give us the tools to address this. Background: The missing scrollbar Compose has always lacked built-in scroll indicators. While you could build custom solutions using drawWithContent and LazyListState.layoutInfo (as many developers have done), there wa

## Smooth Theme Transitions in Compose with Animated ColorSchemes

DevFeed: [Smooth Theme Transitions in Compose with Animated ColorSchemes](<https://devfeed.tech/articles/smooth-theme-transitions-in-compose-with-animated-colorschemes-25120.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2025/07/01/smooth-theme-transitions-in-compose-with-animated-colorschemes/>)

Author: Michael Evans

Published: 2025-07-02T03:38:27Z

Content type: article

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Jetpack Compose](<https://devfeed.tech/topics/jetpack-compose.md>), [Compose](<https://devfeed.tech/topics/compose.md>), [Dark Mode](<https://devfeed.tech/topics/dark-mode.md>), [ui](<https://devfeed.tech/topics/ui.md>), [Code](<https://devfeed.tech/topics/code.md>), [GitHub](<https://devfeed.tech/topics/github.md>)

Tags: [animation](<https://devfeed.tech/tags/animation.md>), [code](<https://devfeed.tech/tags/code.md>), [compose](<https://devfeed.tech/tags/compose.md>), [dark-mode](<https://devfeed.tech/tags/dark-mode.md>), [github](<https://devfeed.tech/tags/github.md>), [jetpack-compose](<https://devfeed.tech/tags/jetpack-compose.md>), [themes](<https://devfeed.tech/tags/themes.md>), [ui](<https://devfeed.tech/tags/ui.md>)

### AI overview

This article explains how to create smooth light and dark theme transitions in Jetpack Compose by animating ColorScheme properties. It compares individual color animations with a cleaner updateTransition-based approach that shares timing and easing, reduces scattered animation logic, and remains easier to extend.

### Source excerpt

If your Jetpack Compose app supports light and dark themes, you've probably noticed the default behavior: a sudden cut between color schemes when the system UI mode changes. I think we've all seen theme changes that look like this: Your browser does not support the video tag. We can do better. By wrapping your theme setup with some animation, we can achieve smooth transitions between light and dark themes that feel much more polished -- without rebuilding your entire app structure. The Idea: Animate the ColorScheme Jetpack Compose's MaterialTheme accepts a ColorScheme. If we gradually animate the colors inside that scheme when the system toggles between dark and light, we can fade the colors smoothly across the entire app. Here's a simplified example: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 @Composable fun AnimatedTheme( darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit ) { val targetScheme = if (darkTheme) DarkColorScheme else LightColorScheme val animatedScheme = targetScheme.copy( primary = animateColorAsState(targetScheme.primary).value, background = animateColorAsState(targetScheme.background).value, surface = animateColorAsState(targetScheme.surface).value, onPrimary = animateColorAsState(targetScheme.onPrimary).value, // Add more swatches as needed... ) MaterialTheme( colorScheme = animatedScheme, typography = Typography, shapes = Shapes, content = content ) } You can expand this to include all 20+ color swatches if you want -- but we'll show a better way shortly. The Problem: Too Many Animations Manually animating every color swatch with animateColorAsState works, but: It's verbose It runs a lot of recompositions (even for unused swatches) It can be hard to keep up if ColorScheme changes A Better Approach: Animating with updateTransition A cleaner solution is to animate all properties as part of a single Transition. That way, Compose shares the animation clock and easing, and you're not scattering a dozen independent

## Turning Any Android Callback into a Flow with callbackFlow

DevFeed: [Turning Any Android Callback into a Flow with callbackFlow](<https://devfeed.tech/articles/turning-any-android-callback-into-a-flow-with-callbackflow-25119.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2025/03/22/turning-any-android-callback-into-a-flow-with-callbackflow/>)

Author: Michael Evans

Published: 2025-03-23T03:10:16Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Coroutines](<https://devfeed.tech/topics/coroutines.md>), [Firebase](<https://devfeed.tech/topics/firebase.md>), [reactive](<https://devfeed.tech/topics/reactive.md>), [API](<https://devfeed.tech/topics/api.md>), [Jetpack Compose](<https://devfeed.tech/topics/jetpack-compose.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [api](<https://devfeed.tech/tags/api.md>), [coroutines](<https://devfeed.tech/tags/coroutines.md>), [firebase](<https://devfeed.tech/tags/firebase.md>), [jetpack-compose](<https://devfeed.tech/tags/jetpack-compose.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [reactive](<https://devfeed.tech/tags/reactive.md>), [realtime-database](<https://devfeed.tech/tags/realtime-database.md>)

### AI overview

This tutorial explains how to use Kotlin's callbackFlow to adapt callback- or listener-based Android APIs into reactive Flow streams. Using Firebase Realtime Database as an example, it shows how to emit updates, propagate cancellation errors, and remove listeners when collection ends.

### Source excerpt

If you've been working with Android for any amount of time, you've probably run into APIs that expose their results using callbacks or listeners. Whether it's something like LocationManager, a custom SDK, or a third-party service like Firebase, you're often stuck adapting old-school async patterns into your modern reactive code. This approach is especially helpful in apps using Jetpack Compose, coroutines, or unidirectional data flow. Fortunately, Kotlin's callbackFlow makes this much easier. In this post, we'll show how to wrap a listener-based API using callbackFlow, so you can collect updates as a Flow. We'll use the Firebase Realtime Database as an example, but this pattern works for nearly anything. The Problem: Listeners Aren't Reactive Here's the classic way of listening to changes in the Firebase Realtime Database: 1 2 3 4 5 6 7 8 9 10 11 val postListener = object : ValueEventListener { override fun onDataChange(dataSnapshot: DataSnapshot) { val post = dataSnapshot.getValue<Post>() // update UI } override fun onCancelled(error: DatabaseError) { Log.w(TAG, "loadPost:onCancelled", error.toException()) } } postReference.addValueEventListener(postListener) This works fine -- but it's imperative and not easily composable with things like StateFlow, LiveData, or Jetpack Compose. Let's fix that. The Fix: Wrap It with callbackFlow Kotlin's callbackFlow is designed for exactly this kind of situation -- where you need to bridge a listener-based API into a reactive stream. Here's what it looks like for Firebase: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 fun Query.asFlow(): Flow<DataSnapshot> = callbackFlow { val listener = object : ValueEventListener { override fun onDataChange(snapshot: DataSnapshot) { trySend(snapshot).isSuccess // emit each snapshot into the Flow } override fun onCancelled(error: DatabaseError) { close(error.toException()) // cancel the flow on error } } // Start listening for updates addValueEventListener(listener) // Suspend until the flow is closed

## UI Testing Made Easy: The Robot Test Pattern on Android

DevFeed: [UI Testing Made Easy: The Robot Test Pattern on Android](<https://devfeed.tech/articles/ui-testing-made-easy-the-robot-test-pattern-on-android-25118.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2024/12/14/ui-testing-made-easy-the-robot-test-pattern-on-android/>)

Author: Michael Evans

Published: 2024-12-14T15:52:24Z

Content type: article

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Testing](<https://devfeed.tech/topics/testing.md>), [Android](<https://devfeed.tech/topics/android.md>), [ui](<https://devfeed.tech/topics/ui.md>), [Compose](<https://devfeed.tech/topics/compose.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-testing](<https://devfeed.tech/tags/android-testing.md>), [best-practices](<https://devfeed.tech/tags/best-practices.md>), [compose](<https://devfeed.tech/tags/compose.md>), [jetpack-compose](<https://devfeed.tech/tags/jetpack-compose.md>), [patterns](<https://devfeed.tech/tags/patterns.md>), [testing](<https://devfeed.tech/tags/testing.md>), [ui](<https://devfeed.tech/tags/ui.md>), [ui-testing](<https://devfeed.tech/tags/ui-testing.md>)

### AI overview

This guide explains the Robot Testing Pattern for Android UI testing. It describes how robots abstract UI interactions from test logic, improving readability, maintainability, reuse, organization, parallel execution, and stability. It also covers implementations for Jetpack Compose and traditional View-based applications, along with base robots and recommended practices.

### Source excerpt

As Android applications grow in complexity, maintaining a robust testing strategy becomes increasingly challenging. The "Robot Testing Pattern" offers a structured approach to UI testing that can significantly improve your test suite's maintainability and reliability. This guide is designed for Android developers who have basic experience with testing and want to enhance their testing methodology. Understanding the Robot Pattern The Robot Testing Pattern, also known as the Robot Framework or Robot Pattern, is a testing methodology that creates an abstraction layer between your test code and UI interactions. Think of robots as specialized assistants that handle all the UI interactions on behalf of your tests. Key Benefits The Robot Pattern provides several advantages that make it particularly valuable for Android testing: Improved Test Readability: Tests become high-level descriptions of user behavior rather than low-level UI interactions, making them easier to understand and maintain. Enhanced Maintainability: When UI changes occur, updates are needed only in the robot implementation rather than across multiple test files. Code Reusability: Common interactions can be shared across multiple test cases, reducing duplication and ensuring consistency. Better Test Organization: The pattern enforces a clear separation between test logic and UI interaction code. Simplified Parallel Testing: Isolated UI interaction logic enables efficient parallel test execution and better test stability. Implementation Guide Let's explore how to implement the Robot Pattern in both Jetpack Compose and traditional View-based applications. Jetpack Compose Implementation Here's a basic implementation for a login screen using Compose: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 class LoginRobot { private val composeTestRule = createComposeRule() fun enterUsername(username: String) = apply { composeTestRule.onNodeWithContentDescription("UsernameTextField") .performTextInput(us

## Modernizing Your Android App's Data Storage: SharedPreferences to DataStore

DevFeed: [Modernizing Your Android App's Data Storage: SharedPreferences to DataStore](<https://devfeed.tech/articles/modernizing-your-android-app-s-data-storage-sharedpreferences-to-datastore-25117.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2024/10/10/modernizing-your-android-apps-data-storage-sharedpreferences-to-datastore/>)

Author: Michael Evans

Published: 2024-10-10T15:53:57Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [migration](<https://devfeed.tech/topics/migration.md>), [schema-evolution](<https://devfeed.tech/topics/schema-evolution.md>), [reactive](<https://devfeed.tech/topics/reactive.md>), [Coroutines](<https://devfeed.tech/topics/coroutines.md>), [Google](<https://devfeed.tech/topics/google.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [best-practices](<https://devfeed.tech/tags/best-practices.md>), [coroutines](<https://devfeed.tech/tags/coroutines.md>), [flow-api](<https://devfeed.tech/tags/flow-api.md>), [migration](<https://devfeed.tech/tags/migration.md>), [pitfalls](<https://devfeed.tech/tags/pitfalls.md>), [protocol](<https://devfeed.tech/tags/protocol.md>), [reactive](<https://devfeed.tech/tags/reactive.md>), [schema-evolution](<https://devfeed.tech/tags/schema-evolution.md>), [testing](<https://devfeed.tech/tags/testing.md>)

### AI overview

This tutorial explains how to migrate Android app data from SharedPreferences to Proto DataStore. It covers dependencies, schema definition, serialization, repository design, migration testing, schema evolution, error handling, performance, and testing practices.

### Source excerpt

SharedPreferences has long been a staple for storing small pieces of data and user preferences in Android apps. However, it has notable limitations, such as a lack of type safety, no support for safe schema evolution, and potential performance issues on the main thread. Google introduced Proto DataStore as a modern and robust alternative, offering: Strong typing with Protocol Buffers Safe schema evolution Built-in migration support Flow API for reactive programming Coroutines support for main-thread safety In this post, we'll walk through the process of migrating your existing SharedPreferences data to Proto DataStore without data loss, including best practices and common pitfalls to avoid. Step 1: Add Dependencies First, add the necessary dependencies to your app's build.gradle file: 1 2 3 4 5 6 7 8 9 dependencies { def datastore_version = "1.0.0" // Proto DataStore implementation "androidx.datastore:datastore:$datastore_version" // Protocol Buffers implementation "com.google.protobuf:protobuf-javalite:3.18.0" } Step 2: Define Your Proto DataStore Schema Create a new .proto file in app/src/main/proto/my_data.proto to define your data schema: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 syntax = "proto3"; option java_package = "com.example.app"; option java_multiple_files = true; message UserPreferences { // Define your fields with unique numbers string user_name = 1; bool notifications_enabled = 2; string theme = 3; // Optional: Add a version field for future schema evolution int32 schema_version = 999; } Note the schema_version field - this helps manage schema evolution as your app grows. Step 3: Create a Proto DataStore Serializer The serializer handles reading and writing your protocol buffer messages: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 object UserPreferencesSerializer : Serializer<UserPreferences> { override val defaultValue: UserPreferences = UserPreferences.getDefaultInstance() override suspend fun readFrom(input: InputStream): UserPreferences { try { return UserPrefere

## Mastering ktlint: A Guide to Crafting Your Own Rules

DevFeed: [Mastering ktlint: A Guide to Crafting Your Own Rules](<https://devfeed.tech/articles/mastering-ktlint-a-guide-to-crafting-your-own-rules-25116.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2024/09/07/mastering-ktlint-a-guide-to-crafting-your-own-rules/>)

Author: Michael Evans

Published: 2024-09-07T15:49:37Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Code quality](<https://devfeed.tech/topics/code-quality.md>), [Testing](<https://devfeed.tech/topics/testing.md>), [Gradle](<https://devfeed.tech/topics/gradle.md>), [Android](<https://devfeed.tech/topics/android.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [code-quality](<https://devfeed.tech/tags/code-quality.md>), [coding](<https://devfeed.tech/tags/coding.md>), [gradle](<https://devfeed.tech/tags/gradle.md>), [gradle-plugin](<https://devfeed.tech/tags/gradle-plugin.md>), [guide](<https://devfeed.tech/tags/guide.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [module](<https://devfeed.tech/tags/module.md>), [test](<https://devfeed.tech/tags/test.md>), [tutorial](<https://devfeed.tech/tags/tutorial.md>)

### AI overview

This tutorial explains how to create a custom ktlint rule for Kotlin projects. The example detects Android Log statements, shows how to configure the ktlint Gradle plugin and dependencies, defines the rule, adds a test, integrates it into ktlint configuration, and runs ktlint to report findings.

### Source excerpt

Ktlint is a powerful linting tool for Kotlin code that helps maintain code quality and consistency. While it comes with a set of built-in rules, there may be cases where you want to create custom rules tailored to your project's specific requirements. In this tutorial, we'll walk you through the process of writing a custom ktlint rule that detects and removes Android Log statements from your Kotlin code. Prerequisites Before we dive into writing custom ktlint rules, ensure you have ktlint installed in your project. The tasks ktlintApplyToIdea and addKtlintCheckTask are provided by the ktlint Gradle plugin. If you haven't already, include the plugin in your project by adding the following to your build.gradle.kts file: 1 2 3 plugins { id("org.jlleitschuh.gradle.ktlint") version "<latest-version>" } Once the plugin is applied, run the following command to set up ktlint in your project: 1 ./gradlew ktlintApplyToIdea addKtlintCheckTask Writing a Custom ktlint Rule 1. Create a New Module To write a custom ktlint rule, start by creating a new module in your Kotlin project. 2. Set Up Your Project In your new module, make sure you have ktlint as a dependency. Add it to your build.gradle.kts or build.gradle file: 1 2 3 dependencies { ktlint("io.gitlab.arturbosch.detekt:detekt-formatting:<ktlint-version>") } 3. Define the ktlint Rule Now, let's define our custom rule. Create a Kotlin class that extends the Rule class and override the visit method to define the logic for your rule. In this example, we want to detect Android Log statements. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import io.gitlab.arturbosch.detekt.api.Rule import org.jetbrains.kotlin.psi.KtCallExpression class LogStatementRule : Rule() { override fun visitCallExpression(expression: KtCallExpression) { if (expression.calleeExpression?.text == "Log" && expression.valueArguments.size == 1 ) { // Report a finding report( finding = "Found Android Log statement", documentable = expression ) } } } 4. Create a Test As w

## Stop Repeating Yourself 2: Using Test Fixtures with AGP

DevFeed: [Stop Repeating Yourself 2: Using Test Fixtures with AGP](<https://devfeed.tech/articles/stop-repeating-yourself-2-using-test-fixtures-with-agp-25115.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2024/07/29/stop-repeating-yourself-2-using-test-fixtures-with-agp/>)

Author: Michael Evans

Published: 2024-07-29T23:43:27Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Android Gradle Plugin](<https://devfeed.tech/topics/android-gradle-plugin.md>), [Testing](<https://devfeed.tech/topics/testing.md>), [Gradle](<https://devfeed.tech/topics/gradle.md>), [build performance](<https://devfeed.tech/topics/build-performance.md>), [Mobile](<https://devfeed.tech/topics/mobile.md>)

Tags: [android-development](<https://devfeed.tech/tags/android-development.md>), [android-gradle-plugin](<https://devfeed.tech/tags/android-gradle-plugin.md>), [build](<https://devfeed.tech/tags/build.md>), [build-performance](<https://devfeed.tech/tags/build-performance.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [gradle](<https://devfeed.tech/tags/gradle.md>), [plugin](<https://devfeed.tech/tags/plugin.md>), [testing](<https://devfeed.tech/tags/testing.md>), [tests](<https://devfeed.tech/tags/tests.md>)

### AI overview

This tutorial explains how Android Gradle Plugin 8.5.0 adds native support for reusable test fixtures in modularized Android projects. It covers defining shared test utilities such as mocks, fakes, and test data, then consuming them from other modules.

### Source excerpt

Modern Android development often involves modularization to enhance build performance and maintainability. As you break down your app into multiple modules, sharing test utilities such as fixtures, mocks, or fakes between modules can become challenging. A few years ago I wrote about the concept of test fixtures, but at the time they weren't supported for Android modules. Thankfully, with Android Gradle Plugin (AGP) 8.5.0, Google introduced native support for test fixtures, streamlining this process significantly. What Are Test Fixtures? Test fixtures are reusable components such as test data, helper classes, or mocks that you can use to support your tests. Prior to AGP 8.5.0, sharing these utilities across modules often required workarounds like creating dedicated "test" modules or manually wiring dependencies. With AGP 8.5.0, the testFixtures feature makes it easier to declare and consume test fixtures directly from your modules. Setting Up Test Fixtures To enable test fixtures for a module, you need to include the testFixtures feature in your module's build.gradle.kts file. Here's how you can set it up: 1 2 3 4 5 6 7 8 9 android { // Enable test fixtures for the module testFixtures.enable = true } dependencies { // Declare dependencies for your test fixtures testFixturesImplementation("com.example:some-library:1.0.0") } The testFixtures source set is automatically created under the src directory: 1 2 3 4 5 6 7 8 9 module-name/ src/ main/ java/ testFixtures/ java/ kotlin/ test/ java/ You can now place reusable test utilities, such as mock data generators or fake implementations, inside the testFixtures directory. Consuming Test Fixtures Modules can consume test fixtures by declaring a dependency on the testFixtures configuration of another module. For example, if module-b needs to use the test fixtures from module-a, add the following dependency: 1 2 3 dependencies { testImplementation(testFixtures(project(":module-a"))) } Once this dependency is added, the test co

## Understanding Referrals using Google Play

DevFeed: [Understanding Referrals using Google Play](<https://devfeed.tech/articles/understanding-referrals-using-google-play-25114.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2024/06/27/understanding-referrals-using-google-play/>)

Author: Michael Evans

Published: 2024-06-27T23:41:33Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [API](<https://devfeed.tech/topics/api.md>), [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Google](<https://devfeed.tech/topics/google.md>), [Coroutines](<https://devfeed.tech/topics/coroutines.md>), [Library](<https://devfeed.tech/topics/library.md>), [App](<https://devfeed.tech/topics/app.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [api](<https://devfeed.tech/tags/api.md>), [app](<https://devfeed.tech/tags/app.md>), [build](<https://devfeed.tech/tags/build.md>), [coroutines](<https://devfeed.tech/tags/coroutines.md>), [data](<https://devfeed.tech/tags/data.md>), [dependency](<https://devfeed.tech/tags/dependency.md>), [google](<https://devfeed.tech/tags/google.md>), [google-play](<https://devfeed.tech/tags/google-play.md>), [guide](<https://devfeed.tech/tags/guide.md>), [how-to](<https://devfeed.tech/tags/how-to.md>), [installreferrerclient](<https://devfeed.tech/tags/installreferrerclient.md>), [integration](<https://devfeed.tech/tags/integration.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [library](<https://devfeed.tech/tags/library.md>), [marketing](<https://devfeed.tech/tags/marketing.md>), [play-store](<https://devfeed.tech/tags/play-store.md>), [prevent-fraud](<https://devfeed.tech/tags/prevent-fraud.md>)

### AI overview

A tutorial on using the Google Play Install Referrer API in an Android application. It explains how install referrer data supports campaign attribution, marketing measurement, and fraud prevention, then shows how to integrate InstallReferrerClient with Kotlin callbackFlow and collect the resulting flow.

### Source excerpt

Tracking how users install your app can be tricky, especially when you want to measure the effectiveness of your campaigns or ads. That's where the Google Play Install Referrer API comes in handy--it gives you reliable data about how users found your app. If you're developing an Android application, this guide will walk you through how to set up and use the InstallReferrerClient with Kotlin's callbackFlow to make integration simpler and more maintainable. What is the Install Referrer? The install referrer contains information about the source of the app installation. For example, if a user clicks an ad campaign link that leads to your app's Play Store page, the referrer might include details about the campaign source, medium, or other specifics. Here's how this data can help: Attribution: Identify which campaigns drive installs. Measure Success: Evaluate your marketing efforts' impact. Prevent Fraud: Verify referrer data to avoid fraudulent installs. Setting Up and Using the InstallReferrerClient Step 1: Add the Dependency First, include the Install Referrer library in your build.gradle file: 1 implementation 'com.android.installreferrer:installreferrer:2.2' Step 2: Use callbackFlow for our implementation The InstallReferrerClient is callback-based, but Kotlin's callbackFlow lets you handle this more elegantly. Here's an example: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import com.android.installreferrer.api.InstallReferrerClient import com.android.installreferrer.api.InstallReferrerStateListener import com.android.installreferrer.api.ReferrerDetails import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.flow.callbackFlow fun fetchInstallReferrer(context: Context) = callbackFlow { val referrerClient = InstallReferrerClient.newBuilder(context).build() val listener = object : InstallReferrerStateListener { override fun onInstallReferrerSetupFinished(responseCode: Int) { when (r

## Accessing Test Resources using Kotlin Multiplatform

DevFeed: [Accessing Test Resources using Kotlin Multiplatform](<https://devfeed.tech/articles/accessing-test-resources-using-kotlin-multiplatform-25113.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2024/04/17/accessing-test-resources-using-kotlin-multiplatform/>)

Author: Michael Evans

Published: 2024-04-18T00:11:01Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Kotlin Multiplatform](<https://devfeed.tech/topics/kotlin-multiplatform.md>), [multiplatform](<https://devfeed.tech/topics/multiplatform.md>), [Testing](<https://devfeed.tech/topics/testing.md>), [Android](<https://devfeed.tech/topics/android.md>), [iOS](<https://devfeed.tech/topics/ios.md>), [JSON](<https://devfeed.tech/topics/json.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [code](<https://devfeed.tech/tags/code.md>), [ios](<https://devfeed.tech/tags/ios.md>), [json](<https://devfeed.tech/tags/json.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [kotlin-multiplatform](<https://devfeed.tech/tags/kotlin-multiplatform.md>), [library](<https://devfeed.tech/tags/library.md>), [multiplatform](<https://devfeed.tech/tags/multiplatform.md>), [test](<https://devfeed.tech/tags/test.md>), [testing](<https://devfeed.tech/tags/testing.md>)

### AI overview

This tutorial explains how to access test resources such as JSON files and configuration data in Kotlin Multiplatform projects. It describes organizing resources per target platform and using platform-specific loaders with shared test logic.

### Source excerpt

Kotlin Multiplatform (KMP) offers a powerful way to share code across platforms like Android, iOS, and the JVM. However, when it comes to testing, you might encounter a common challenge: how to handle test resources such as JSON files, configurations, or other data needed for tests. In this post, we'll dive into strategies for accessing test resources in KMP projects. Why Test Resources Matter Test resources are essential for validating your code against real-world scenarios. For example, if you're writing a library to parse JSON, you'd want to test it against diverse JSON samples representing different edge cases. While resource access is straightforward in single-platform projects, KMP's multi-target nature requires some additional setup. The Challenge in Kotlin Multiplatform In KMP, test code resides in the commonTest source set, but resources aren't directly bundled with it. Each platform manages file paths and resource access differently, so you need platform-specific setups for your resources and shared logic to load them efficiently. Organizing Test Resources Start by structuring your resources in a way that aligns with your project layout: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 project-root/ src/ commonTest/ kotlin/ ... androidTest/ resources/ test-data.json jvmTest/ resources/ test-data.json iosTest/ resources/ test-data.json This setup ensures that each target platform can access its respective resources while keeping them logically grouped. Loading Resources by Platform For Android, place your test resources in the androidTest/resources directory. Use the javaClass.classLoader to load them: 1 2 3 4 5 fun loadTestResource(resourceName: String): String { val inputStream = javaClass.classLoader?.getResourceAsStream(resourceName) return inputStream?.bufferedReader()?.use { it.readText() } ?: throw IllegalArgumentException("Resource not found: $resourceName") } For iOS, add your resources to the test target in Xcode. Use platform-specific code to load them: 1 2 3 4

## Building Flipper Plugins for Fun and Profit

DevFeed: [Building Flipper Plugins for Fun and Profit](<https://devfeed.tech/articles/building-flipper-plugins-for-fun-and-profit-25112.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2022/09/28/flipper-plugins-for-fun-and-profit/>)

Author: Michael Evans

Published: 2022-09-29T03:41:17Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Firebase](<https://devfeed.tech/topics/firebase.md>), [Tooling](<https://devfeed.tech/topics/tooling.md>), [Wireless Debugging](<https://devfeed.tech/topics/wireless-debugging.md>), [Gradle](<https://devfeed.tech/topics/gradle.md>)

Tags: [adb](<https://devfeed.tech/tags/adb.md>), [analytics](<https://devfeed.tech/tags/analytics.md>), [android](<https://devfeed.tech/tags/android.md>), [debugging](<https://devfeed.tech/tags/debugging.md>), [development-tools](<https://devfeed.tech/tags/development-tools.md>), [firebase](<https://devfeed.tech/tags/firebase.md>), [gradle](<https://devfeed.tech/tags/gradle.md>), [payload](<https://devfeed.tech/tags/payload.md>), [sdk](<https://devfeed.tech/tags/sdk.md>)

### AI overview

This tutorial explains how to build a custom Flipper plugin for Android to inspect Firebase Analytics events in a filterable table. It contrasts this workflow with using ADB and Logcat, and briefly introduces the client-side setup, including the Flipper SDK and Gradle dependencies.

### Source excerpt

A long, long time ago, I wrote a blog post about how I was using Flipper as one of my favorite development tools. Since then, Android Studio has come a long way adding tons of features like a new Logcat and Layout Inspector. However, there are often times that you'll need a tool more specific to your own workflow that Android Studio doesn't provide, and that's exactly where Flipper's extensibility really shines. As an example, I'd like to go through building a custom plugin for Flipper, similar to one that I've used on my own projects, that demonstrates how easy it is to get started building these tools. The Problem As most apps grow, there becomes a need to measure app usage and engagement to better understand user behavior. In order to measure that, we often turn to analytics libraries (like Firebase Analytics) to handle this in-app behavior reporting. However, when implementing these client events, it's often helpful to have a quick feedback loop to ensure that the event and associated payload are correct, without having to check an analytics dashboard (which can often take some time to refresh). Luckily, most analytics libraries (including Firebase) have different solutions for this problem. In the Firebase Analytics library, the recommended debugging method is to set a property with ADB to log all the events to logcat. This does provide much faster feedback than checking a dashboard, but it's not the most user friendly - developers need to set the property at the command line, and need to be monitoring logcat for all of the events (and also doesn't offer much of a search/filter function). The Solution Rather than sticking to plain text in logcat, we can build a custom Flipper plugin that will display our analytics events in a filterable table. Most Flipper plugins are comprised of two parts - a client library that runs as part of your Android app, and a desktop plugin that runs inside Flipper for processing and displaying the data sent by the client. All of the

## Android Developer Challenge

DevFeed: [Android Developer Challenge](<https://devfeed.tech/articles/android-developer-challenge-25111.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2020/06/21/android-developer-challenge/>)

Author: Michael Evans

Published: 2020-06-22T03:41:17Z

Content type: article

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Machine Learning & Artificial Intelligence](<https://devfeed.tech/topics/machine-learning-artificial-intelligence.md>), [App](<https://devfeed.tech/topics/app.md>), [Google](<https://devfeed.tech/topics/google.md>), [object-detection](<https://devfeed.tech/topics/object-detection.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [apps](<https://devfeed.tech/tags/apps.md>), [camera](<https://devfeed.tech/tags/camera.md>), [developer](<https://devfeed.tech/tags/developer.md>), [google](<https://devfeed.tech/tags/google.md>), [machine-learning](<https://devfeed.tech/tags/machine-learning.md>), [map](<https://devfeed.tech/tags/map.md>), [object](<https://devfeed.tech/tags/object.md>), [on-device](<https://devfeed.tech/tags/on-device.md>)

### AI overview

The article reviews several winning apps from the Android Developer Challenge, highlighting how on-device machine learning enables camera-based object recognition, American Sign Language learning, and obstacle heatmaps for visually impaired people.

### Source excerpt

Late last year, Google announced the Android Developer Challenge, a contest for Android developers to show off new experiences made possible by on-device Machine Learning. Since then, tons of developers have submitted their ideas and been hard at work developing their apps. Today, the winners of the challenge have been announced! I was lucky to get access to a cool trial box that Google sent out, complete with little goodies to try out some of the apps from the winners! Here are some obligatory unboxing photos: After checking out the cool loot, I downloaded the winning apps to check them out, and wanted to show off some of my favorites. Trashly The first app I tried was Trashly. The goal of this app is to make recycling easier by providing up-to-date information about where and how to recycle your items. You can type in any item that you're interested in recycling, but what's cooler (and relevant to the challenge) is that you can use the camera to detect an object and find out 1) if the item is recyclable, and 2) where you can go to recycle it. I tried this with a can of soda, which was instantly recognized: And was given a map of nearby places that I could take my can to recycle. Very cool and useful! Leepi The next app that I tried was Leepi. It's a fun, educational app to help users learn American Sign Language. I personally had never learned Sign Language, so this was a really cool way to start! It uses the camera and on-device machine learning to interpret the user's hand positions to verify that they are doing the hand positions and gestures correctly. Path Finder The last app I wanted to talk about was called Path Finder. The gist of this app is to use the camera and machine learning to build a heatmap of obstacles that might be problematic for visually impaired people in public environments. I tried the app out on the streets of New York City and have some screenshots of the results below. I am not sure how useful this would be in practice, but it certainly

## Improving App Debugging with Flipper

DevFeed: [Improving App Debugging with Flipper](<https://devfeed.tech/articles/improving-app-debugging-with-flipper-25110.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2020/03/10/improving-app-debugging-with-flipper/>)

Author: Michael Evans

Published: 2020-03-11T00:10:22Z

Content type: article

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [debug](<https://devfeed.tech/topics/debug.md>), [Android](<https://devfeed.tech/topics/android.md>), [Mobile](<https://devfeed.tech/topics/mobile.md>), [Database](<https://devfeed.tech/topics/database.md>), [SQLite](<https://devfeed.tech/topics/sqlite.md>), [ide](<https://devfeed.tech/topics/ide.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-studio](<https://devfeed.tech/tags/android-studio.md>), [api](<https://devfeed.tech/tags/api.md>), [database](<https://devfeed.tech/tags/database.md>), [debugging](<https://devfeed.tech/tags/debugging.md>), [plugins](<https://devfeed.tech/tags/plugins.md>), [sqlite](<https://devfeed.tech/tags/sqlite.md>)

### AI overview

This article introduces Flipper, Facebook's mobile debugging tool and successor to Stetho. It explains how to set up Flipper in an Android application and demonstrates its Inspector and database plugins for real-time layout inspection, property experimentation, database browsing, and live SQL queries.

### Source excerpt

Some time last year, Facebook released a new mobile debugging tool, named Flipper. It's essentially the successor to the widely popular Stetho. Although after talking to many developers, it seems like this newer tool is relatively unknown. Like Stetho, Flipper has many built-in features - including a layout inspector, a database inspector and a network inspector. Unlike Stetho though, Flipper has a very extensible API which allows for tons of customization. Over the next few articles, we're going to take a look at Flipper and its plugins, the APIs it provides, and how we can leverage them to help us debug various parts of our app. This post will focus on getting set up with Flipper, as well as taking a look at two of its most useful default plugins. Getting Started Getting started with Flipper is really easy: Download the desktop client, Add the dependencies in your build.gradle: 1 2 3 4 5 6 7 8 9 10 repositories { jcenter() } dependencies { debugImplementation 'com.facebook.flipper:flipper:0.33.1' debugImplementation 'com.facebook.soloader:soloader:0.8.2' releaseImplementation 'com.facebook.flipper:flipper-noop:0.33.1' } Initialize the Flipper client when your application starts: 1 2 3 4 5 6 7 8 9 10 11 12 class SampleApplication : Application() { override fun onCreate() { super.onCreate() SoLoader.init(this, false) if (BuildConfig.DEBUG && FlipperUtils.shouldEnableFlipper(this)) { val client = AndroidFlipperClient.getInstance(this) client.addPlugin(InspectorFlipperPlugin(this, DescriptorMapping.withDefaults())) client.start() } } } And that's it! Opening the desktop client should show you an overview of your app with the Inspector plugin configured. Inspector Plugin The Inspector Plugin is similar to the one found in Android Studio 4.0, but has a few neat features. I like it because it operates in real-time, and doesn't require any attaching to process in Studio every time you want to inspect a layout. Another thing you can do in the Layout Inspector that's really

## Dropping Columns Like It's Hot

DevFeed: [Dropping Columns Like It's Hot](<https://devfeed.tech/articles/dropping-columns-like-it-s-hot-25109.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2020/02/10/dropping-columns-like-its-hot/>)

Author: Michael Evans

Published: 2020-02-11T01:03:03Z

Content type: article

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [SQLite](<https://devfeed.tech/topics/sqlite.md>), [Structured-data](<https://devfeed.tech/topics/structured-data.md>), [migration](<https://devfeed.tech/topics/migration.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [code](<https://devfeed.tech/tags/code.md>), [database](<https://devfeed.tech/tags/database.md>), [migration](<https://devfeed.tech/tags/migration.md>), [schema](<https://devfeed.tech/tags/schema.md>), [sqlite](<https://devfeed.tech/tags/sqlite.md>)

### AI overview

This article explains why dropping a column in SQLite can fail when a migration library emulates the operation by renaming the table, creating a replacement table, copying the retained data, and deleting the old table. It highlights how SQLite versions 3.25.0 and 3.26.0 changed table-rename behavior for triggers and views, which can affect this migration process.

### Source excerpt

Recently, I was doing some code cleanup and noticed that there were some data in the database that was no longer needed. I think most developers clean up their codebase of deprecated patterns and unused code, but I personally have not done a good job of ensuring that the same cleanup happens for unused columns in my databases. Dropping tables that are no longer used is pretty easy (especially if you can just use something like Room's Migrations) but when trying to remove unused columns, I ran into an unexpected problem. I thought to myself, it's pretty easy to add or rename a column, why would dropping one be any harder? The existing database library I was using already had a convenient "drop column" method, so I simply called that and tried to run the migration. During the process, I ended up with a ForeignKeyConstraintException! I quickly scanned the schema to see what could have caused that, and didn't see anything obvious. The table I was trying to modify didn't have any foreign keys itself, and the column I was dropping was not a foreign key. Curious to understand what was happening, I started to dig into what this method call was doing. I saw that although you can add a column with SQLite's ALTER TABLE ${tableName} ADD COLUMN ${columnName} ${columnType} statements, there's no support for removing a column out of the box. The library method I was using emulates dropping a column by doing the following: Rename the existing table into $tablename_old Creating a new table with all the existing columns, minus the one we don't want Copying all the data from $tablename_old to $tablename Dropping $tablename_old, since we don't need it anymore. This process seems to make a lot of sense - since we can't remove the column on its own, let's just make a new table with the structure we want and copy over the data that we want to keep. So why does this process not work? The Gotcha! If you read the SQlite documentation linked above closely, you might have noticed an important

## Stop Repeating Yourself: Sharing test code across Android Modules

DevFeed: [Stop Repeating Yourself: Sharing test code across Android Modules](<https://devfeed.tech/articles/stop-repeating-yourself-sharing-test-code-across-android-modules-25108.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2019/09/21/stop-repeating-yourself-sharing-test-code-across-android-modules/>)

Author: Michael Evans

Published: 2019-09-22T01:34:52Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Gradle](<https://devfeed.tech/topics/gradle.md>), [Testing](<https://devfeed.tech/topics/testing.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [build](<https://devfeed.tech/tags/build.md>), [dependency](<https://devfeed.tech/tags/dependency.md>), [gradle](<https://devfeed.tech/tags/gradle.md>), [testing](<https://devfeed.tech/tags/testing.md>), [tests](<https://devfeed.tech/tags/tests.md>)

### AI overview

This tutorial explains how to share test fixtures across Android modules using Gradle's test-fixtures plugin, introduced in Gradle 5.6. It describes applying the plugin, placing fixtures in the appropriate source set, and declaring them as test dependencies.

### Source excerpt

It seems like nowadays, the best advice is to modularize your Android app. It's a great suggestion for many reasons, including but not limited to: - improved build performance - enables on-demand delivery - pushes you to build reusable, discrete components Sounds great, right? Are there any downsides? There is one in particular which has been a a pain point for many. Often times when you're writing tests, you'll want to use some test doubles like fakes or fixtures in order to help simulate the system under test. Maybe you have a FakeUser instance that you use in your tests to avoid having to mock a User every time your test calls for one. Generally these classes live alongside tests in src/test directories and are used to test out your code within a module. For example, maybe you have a model object like: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public class User { private final String firstName; private final String lastName; public User(String firstName, String lastName) { this.firstName = firstName; this.lastName = lastName; } public String getFirstName() { return firstName; } public String getLastName() { return lastName; } } You might have some code in src/test that creates a bunch of fake users for your tests like: 1 2 3 4 5 class TheOfficeFixtures { public static User manager = new User("Michael", "Scott"); public static User assistantToTheRegionalManager = new User("Dwight", "Schrute"); } } This works great if you're testing code within a module, but as soon as you'd like to use these fake users in other modules, you'll note that these classes aren't shared! This code can't be shared between modules because Gradle doesn't expose the output of your test source set as a build artifact. There are all kinds of solutions for this problem out there, including creating a special module for all your fixtures, and using gradle dependency hacks to wire up source sets. However, that's not necessary anymore! As of version 5.6, Gradle now ships a new 'test-fixtures' plu

## Hands on with ViewPager2

DevFeed: [Hands on with ViewPager2](<https://devfeed.tech/articles/hands-on-with-viewpager2-25107.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2019/02/07/hands-on-with-viewpager2/>)

Author: Michael Evans

Published: 2019-02-08T02:38:07Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Library](<https://devfeed.tech/topics/library.md>), [Code](<https://devfeed.tech/topics/code.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [build](<https://devfeed.tech/tags/build.md>), [code](<https://devfeed.tech/tags/code.md>), [components](<https://devfeed.tech/tags/components.md>), [dependencies](<https://devfeed.tech/tags/dependencies.md>), [google](<https://devfeed.tech/tags/google.md>), [layout](<https://devfeed.tech/tags/layout.md>), [library](<https://devfeed.tech/tags/library.md>), [open-source](<https://devfeed.tech/tags/open-source.md>), [project](<https://devfeed.tech/tags/project.md>)

### AI overview

A hands-on introduction to Google's alpha release of ViewPager2, explaining its RecyclerView-based setup, adapter implementation, dependency configuration, and vertical scrolling support.

### Source excerpt

Today Google released their alpha of ViewPager2, a signal of the nail in the coffin for the original ViewPager, originally released in 2011! Since then, I think it's safe to say that most developers have needed to make a ViewPager. Despite how prolific it is, it certainly isn't the most straightforward widget to include. I think we all have at least once wondered whether we should use a FragmentPagerAdapter or a FragmentStatePagerAdapter. Or wondered if we can use a ViewPager without Fragments. And API confusion aside, we've still had long standing, feature requests. RTL support? Vertical orientation? There are numerous open source solutions for these, but nothing official from the support library (now AndroidX)...until now! Let's dive in and try to set up ViewPager2! You'll need your project configured with AndroidX already, as well as supporting minSdkVersion 14 or higher. The first thing we'll need to do is add the library to our build.gradle dependencies. 1 implementation 'androidx.viewpager2:viewpager2:1.0.0-alpha01' If you're familiar with RecyclerView, setting up ViewPager2 will be very familiar. We start off by creating an adapter: 1 2 3 4 5 6 7 8 9 10 11 class CheesePagerAdapter(private val cheeseStrings: Array<String>) : RecyclerView.Adapter<CheeseViewHolder>() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CheeseViewHolder { return CheeseViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.cheese_list_item, parent, false)) } override fun onBindViewHolder(holder: CheeseViewHolder, position: Int) { holder.cheeseName.text = cheeseStrings[position] } override fun getItemCount() = cheeseStrings.size } and pair it with a RecyclerView.ViewHolder. 1 2 3 4 class CheeseViewHolder(view: View) : RecyclerView.ViewHolder(view) { val cheeseName: TextView = view.findViewById(R.id.cheese_name) } Finally, just like RecyclerView, we set the adapter of our ViewPager2 to be an instance of the RecyclerView adapter. However, you'll note that there

## Enabling Night Mode on Android Nougat

DevFeed: [Enabling Night Mode on Android Nougat](<https://devfeed.tech/articles/enabling-night-mode-on-android-nougat-25106.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2016/08/23/enabling-night-mode-on-android-nougat/>)

Author: Michael Evans

Published: 2016-08-23T22:27:01Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Android](<https://devfeed.tech/topics/android.md>), [Code](<https://devfeed.tech/topics/code.md>), [Shell](<https://devfeed.tech/topics/shell.md>), [Command-line interface](<https://devfeed.tech/topics/cli.md>), [App](<https://devfeed.tech/topics/app.md>), [ui](<https://devfeed.tech/topics/ui.md>)

Tags: [adb](<https://devfeed.tech/tags/adb.md>), [android](<https://devfeed.tech/tags/android.md>), [app](<https://devfeed.tech/tags/app.md>), [code](<https://devfeed.tech/tags/code.md>), [developer](<https://devfeed.tech/tags/developer.md>), [feature](<https://devfeed.tech/tags/feature.md>), [play-store](<https://devfeed.tech/tags/play-store.md>), [source](<https://devfeed.tech/tags/source.md>), [ui](<https://devfeed.tech/tags/ui.md>)

### AI overview

This tutorial explains how to restore access to Night Mode settings on Android Nougat by launching the System UI Tuner activity with the appropriate intent extra through adb. It also presents a Play Store app that performs the process with a single button.

### Source excerpt

If you're like me, you loved the Night Mode feature that was added to the Nougat Developer Preview a few months ago. You might have been disappointed when you found out that it was missing in later preview builds, and was probably going to be removed because it wasn't ready. When the source code for Nougat was released this morning, my friend Vishnu found this interesting snippet in the SystemUI source (better known to end users as the System UI Tuner): 1 2 3 4 5 boolean showNightMode = getIntent().getBooleanExtra( NightModeFragment.EXTRA_SHOW_NIGHT_MODE, false); final PreferenceFragment fragment = showNightMode ? new NightModeFragment() : showDemoMode ? new DemoModeFragment() : new TunerFragment(); Long story short, if you pass the right extras to this activity, and you'll get access to the Night Mode settings (as well as the infamous Quick Tile!). Fortunately for us, this is pretty trivial to accomplish with adb via adb -d shell am start --ez show_night_mode true com.android.systemui/.tuner.TunerActivity, but not everyone who wants this feature is familiar with adb. So I published an app to the Play Store that does exactly that - click one button, and get access to those settings! You can find the app on the Play Store here.

## Using Build Types with the Google Services Gradle Plugin

DevFeed: [Using Build Types with the Google Services Gradle Plugin](<https://devfeed.tech/articles/using-build-types-with-the-google-services-gradle-plugin-25105.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2016/03/31/using-build-types-with-the-google-services-gradle-plugin/>)

Author: Michael Evans

Published: 2016-04-01T01:11:55Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Gradle](<https://devfeed.tech/topics/gradle.md>), [Android](<https://devfeed.tech/topics/android.md>), [Google](<https://devfeed.tech/topics/google.md>), [Groovy](<https://devfeed.tech/topics/groovy.md>), [JSON](<https://devfeed.tech/topics/json.md>), [API keys](<https://devfeed.tech/topics/api-keys.md>), [configuration](<https://devfeed.tech/topics/configuration.md>), [Development](<https://devfeed.tech/topics/development.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [api-keys](<https://devfeed.tech/tags/api-keys.md>), [code](<https://devfeed.tech/tags/code.md>), [configuration](<https://devfeed.tech/tags/configuration.md>), [development](<https://devfeed.tech/tags/development.md>), [google](<https://devfeed.tech/tags/google.md>), [gradle](<https://devfeed.tech/tags/gradle.md>), [gradle-plugin](<https://devfeed.tech/tags/gradle-plugin.md>), [groovy](<https://devfeed.tech/tags/groovy.md>), [json](<https://devfeed.tech/tags/json.md>)

### AI overview

This tutorial explains how to use different Google Services configuration files for Android debug and release builds when the Google Services Gradle plugin supports build flavors but not build types. It presents a Groovy and Gradle task-based workaround that copies the appropriate JSON file into the app module before the Google Services processing task runs.

### Source excerpt

If you want to integrate your Android app with most of Google Play Services nowadays, you'll find that you are instructed to set up the Google Services Gradle plugin to handle configuring dependencies. The plugin allows you to drop a JSON file into your project, and then the plugin will do a bunch of the configuration for your project, such as handling the API keys. This is all well and good--unless you're like me (and countless others) and want to use a different configuration for your debug and release builds. This would be useful, as an example, if you use Google Play Services for GCM and would like to have development builds recieve pushes from non-production systems. It seems that the plugin is configured in such a way that it supports build flavors, but it does not yet support build types. However, with a little Gradle magic, we can hack that support in. Disclaimer: This approach worked for me--but as with any hack, it is subject to break. So how can we go about doing this? We want to put the debug JSON file into the root of our app module during debug builds and use the release one for release builds. If you don't do that, or if you attempt to put it in app/debug and app/release, you'll get an error that says File google-services.json is missing from module root folder. The Google Services Plugin cannot function without it. This error is thrown by a task named process{VariantName}GoogleServices. What we could do to solve this is swap the file in before that task is run! Using a little Groovy magic, I came up with this: 1 2 3 4 5 6 7 8 9 10 android.applicationVariants.all { variant -> def hackTask = task("hackGps${variant.name.capitalize()}") << { copy { from rootProject.file("config/${variant.buildType.name}/google-services.json") into "${projectDir}" } } def googleTask = tasks.findByName("process${variant.name.capitalize()}GoogleServices") googleTask.dependsOn hackTask } For each one of your variants, this code will create a new task - hackGps{VariantName}, wh

## Changelog for N Support Libraries

DevFeed: [Changelog for N Support Libraries](<https://devfeed.tech/articles/changelog-for-n-support-libraries-25104.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2016/03/09/changelog-for-n-support-libraries/>)

Author: Michael Evans

Published: 2016-03-10T00:38:46Z

Content type: article

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [changelog](<https://devfeed.tech/topics/changelog.md>), [Android](<https://devfeed.tech/topics/android.md>), [Library](<https://devfeed.tech/topics/library.md>), [SDKs](<https://devfeed.tech/topics/sdks.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [api](<https://devfeed.tech/tags/api.md>), [changelog](<https://devfeed.tech/tags/changelog.md>), [developer](<https://devfeed.tech/tags/developer.md>), [libraries](<https://devfeed.tech/tags/libraries.md>)

### AI overview

This article documents public API changes in several Android support libraries released with the Android N Preview. It compares the previous support library version with the new alpha version, including additions to FragmentController, FragmentManagerNonConfig, and FragmentTransaction, while noting that RecyclerView and Support Annotations had no API changes.

### Source excerpt

Pssst! If you're an Android developer, you might not have heard yet...the N Preview started today! As part of the festivities, a new alpha version of the support libraries was released. There was no changelog that I could find, so I decided to make one. Here's what has changed (so far) in the public API of a few of these libraries: Support-V4: diff -U 0 -N support-v4-23.2.0_df13b086/android.support.v4.app.FragmentController support-v4-24.0.0-alpha1_41849fd4/android.support.v4.app.FragmentController --- support-v4-23.2.0_df13b086/android.support.v4.app.FragmentController 2016-03-09 19:28:24.000000000 -0500 +++ support-v4-24.0.0-alpha1_41849fd4/android.support.v4.app.FragmentController 2016-03-09 19:28:24.000000000 -0500 @@ -11,0 +12 @@ + public void restoreAllState(android.os.Parcelable, android.support.v4.app.FragmentManagerNonConfig); @@ -12,0 +14 @@ + public android.support.v4.app.FragmentManagerNonConfig retainNestedNonConfig(); diff -U 0 -N support-v4-23.2.0_df13b086/android.support.v4.app.FragmentManagerNonConfig support-v4-24.0.0-alpha1_41849fd4/android.support.v4.app.FragmentManagerNonConfig --- support-v4-23.2.0_df13b086/android.support.v4.app.FragmentManagerNonConfig 1969-12-31 19:00:00.000000000 -0500 +++ support-v4-24.0.0-alpha1_41849fd4/android.support.v4.app.FragmentManagerNonConfig 2016-03-09 19:28:24.000000000 -0500 @@ -0,0 +1,2 @@ +public class android.support.v4.app.FragmentManagerNonConfig { +} diff -U 0 -N support-v4-23.2.0_df13b086/android.support.v4.app.FragmentTransaction support-v4-24.0.0-alpha1_41849fd4/android.support.v4.app.FragmentTransaction --- support-v4-23.2.0_df13b086/android.support.v4.app.FragmentTransaction 2016-03-09 19:28:24.000000000 -0500 +++ support-v4-24.0.0-alpha1_41849fd4/android.support.v4.app.FragmentTransaction 2016-03-09 19:28:24.000000000 -0500 @@ -34,0 +35,2 @@ + public abstract void commitNow(); + public abstract void commitNowAllowingStateLoss(); diff -U 0 -N support-v4-23.2.0_df13b086/android.support.v4.content.Conte

## Using Dagger 1 and Kotlin

DevFeed: [Using Dagger 1 and Kotlin](<https://devfeed.tech/articles/using-dagger-1-and-kotlin-25103.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2016/02/17/using-dagger-1-and-kotlin/>)

Author: Michael Evans

Published: 2016-02-17T18:43:25Z

Content type: article

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Dagger](<https://devfeed.tech/topics/dagger.md>), [Kotlin](<https://devfeed.tech/topics/kotlin.md>), [Android](<https://devfeed.tech/topics/android.md>), [Compiler](<https://devfeed.tech/topics/compiler.md>), [Development](<https://devfeed.tech/topics/development.md>)

Tags: [2](<https://devfeed.tech/tags/2.md>), [android](<https://devfeed.tech/tags/android.md>), [android-development](<https://devfeed.tech/tags/android-development.md>), [beginner](<https://devfeed.tech/tags/beginner.md>), [compiler](<https://devfeed.tech/tags/compiler.md>), [dagger](<https://devfeed.tech/tags/dagger.md>), [dagger-2](<https://devfeed.tech/tags/dagger-2.md>), [dependencies](<https://devfeed.tech/tags/dependencies.md>), [development](<https://devfeed.tech/tags/development.md>), [di](<https://devfeed.tech/tags/di.md>), [java](<https://devfeed.tech/tags/java.md>), [kotlin](<https://devfeed.tech/tags/kotlin.md>), [property](<https://devfeed.tech/tags/property.md>)

### AI overview

This article explains how to use Dagger 1 with Kotlin in an Android app. It describes Kotlin annotation-processing limitations, Dagger module compilation problems, and a workaround that converts modules to Java classes. It also explains how to inject Kotlin properties by targeting the annotation at the backing field because Dagger does not support method injection.

### Source excerpt

Unless you've been hiding from all the news about Android development, you've likely heard about Kotlin (which hit version 1.0 on Monday!). I've been toying around with it lately (the Kotlin Koans are a great place to start for a beginner) and wanted to try building an app with it - that is, until I hit a few road blocks. Personally, I'm still a fan of Dagger 1 (or as I refer to it, Dagger Classic), and when I started working on my Kotlin app, that's what I was planning to use. I knew Annotation Processing support was a relatively new addition to Kotlin, so I began to search for some information about how to get Dagger to play nicely with the Kotlin compiler. There's a lot of information about using Dagger 2 with Kotlin but not so much about Dagger Classic. Finally, I stumbled upon this article, which said, "Unfortunately, Square's Dagger 1 does not appear to work with Kotlin while Google's Dagger 2 does". Bummer. This didn't really deter me, however, because I'm stubborn like that. So I proceeded to give it a try with kapt1 anyway (which seemed like it might do what I want). Modules The first thing I did was try to create the various Dagger Modules that I'd need, which is where I hit my first roadblock. Attempting to compile my module gave the following error: 1 Error:Modules must not extend from other classes: org.michaelevans.example.AppModule My intial thought was that Kotlin was causing my Module to extend Any, rather than Object. (Any is the root of the class hierarchy in Kotlin, similar to the way that Object is the root of the Java class hierarchy.) Upon closer inspection, that didn't seem to be the issue, but rather than get hung up on this - I just converted my modules to Java classes and decided to come back to this issue later. @Inject So now I had my modules set up, and I went about trying to @Inject some fields on an Activity or two. This yielded another problem: Kotlin doesn't have fields, and we obviously can't do constructor injection on something f

## Android Studio Tips and Tricks

DevFeed: [Android Studio Tips and Tricks](<https://devfeed.tech/articles/android-studio-tips-and-tricks-25102.md>)

Original publisher: [Read original article](<http://michaelevans.org/blog/2016/01/06/android-studio-tips-and-tricks/>)

Author: Michael Evans

Published: 2016-01-07T01:25:53Z

Content type: tutorial

Language: en

Sources: [Gadget Habit](<https://devfeed.tech/sources/gadget-habit.md>)

Topics: [Android Studio](<https://devfeed.tech/topics/android-studio.md>), [IntelliJ IDEA](<https://devfeed.tech/topics/intellij-idea.md>), [code-completion](<https://devfeed.tech/topics/code-completion.md>), [JSON](<https://devfeed.tech/topics/json.md>), [Regular expression](<https://devfeed.tech/topics/regular-expression.md>), [API](<https://devfeed.tech/topics/api.md>)

Tags: [android](<https://devfeed.tech/tags/android.md>), [android-studio](<https://devfeed.tech/tags/android-studio.md>), [code-completion](<https://devfeed.tech/tags/code-completion.md>), [development](<https://devfeed.tech/tags/development.md>), [json](<https://devfeed.tech/tags/json.md>), [productivity](<https://devfeed.tech/tags/productivity.md>), [shortcuts](<https://devfeed.tech/tags/shortcuts.md>), [tips-and-tricks](<https://devfeed.tech/tags/tips-and-tricks.md>)

### AI overview

A practical guide to Android Studio and IntelliJ features that improve coding productivity, including language injection for JSON and regular expressions, context-aware type completion, the Productivity Guide, and IntelliJ's REST client.

### Source excerpt

I recently attended Google's Android Dev Summit where the Tools team presented a talk entitled Android Studio For Experts. The room was packed for the 90 minute session, where a lot of great Android Studio tips were shared. This gave me the idea of showing off some of my favorite Android Studio tips! Language Injection Ever needed to type a JSON String? Perhaps you've used one as a text fixture for one of your GSON deserializers and know that it's a huge pain to manage all those backslashes. Fortunately, IntelliJ has a feature called Language Injection, which allows you to edit the JSON fragment in its own editor, and then IntelliJ will properly inject that fragment into your code as an escaped String. Inject Language/Reference is an intention action1, so you can start it by using ⌥+Return, or ⌘+⇧+A and searching for it. Check RegExp This is pretty similar to the last tip, but if you select the language of the fragment as "RegExp", you'll get a handy regular expression tester! Smart(er) Completion Now I'm pretty sure most of you have used IntelliJ's code completion features. Press ⌥+Space, and IntelliJ/Android Studio lists options to complete the names of classes, methods, fields, and keywords within the visibility scope. But have you ever noticed that the suggestions seem to be based off the characters you've typed, rather than the actual types that are expected in the scope of the caret? Something like this: Well if you use Type Completion (by pressing ⌥+⇧+Space), you will see a list of suggestions containing only those types that are applicable to the current context. In the example below, you'll only get types that return a Reader, which is the type that the BufferedReader's constructor expects: What's even cooler is that you can press it an additional time, and IntelliJ will do a deeper scan (looking at static method calls, chained expressions, etc.) to find more options for you: Discovering Your Own Tips and Tricks Another really cool feature is the Productivi