<?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"><channel><title><![CDATA[Mobile Mayhem]]></title><description><![CDATA[Wrangling the chaos (for me at least) of Android, KMP, and iOS.]]></description><link>https://blog.alphaomardiallo.com</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 17:24:11 GMT</lastBuildDate><atom:link href="https://blog.alphaomardiallo.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Compose Multiplatform Theming: A Comprehensive Guide to Customization]]></title><description><![CDATA[I have been working with Compose Multiplatform for a few days, and I struggle to find the correct resources to learn about theming for Compose Multiplatform. Here is how I do it—it’s not perfect, and it’s not the only solution, but it’s mine.
Gradle ...]]></description><link>https://blog.alphaomardiallo.com/compose-multiplatform-theming-a-comprehensive-guide-to-customization</link><guid isPermaLink="true">https://blog.alphaomardiallo.com/compose-multiplatform-theming-a-comprehensive-guide-to-customization</guid><category><![CDATA[compose multiplatform]]></category><category><![CDATA[Kotlin Multiplatform]]></category><category><![CDATA[Kotlin]]></category><category><![CDATA[iOS]]></category><category><![CDATA[Android]]></category><dc:creator><![CDATA[Alpha Diallo]]></dc:creator><pubDate>Tue, 17 Dec 2024 12:19:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/o1SKqmgSDbg/upload/021ff0738ccebab3dddb1dfdba700bcf.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I have been working with Compose Multiplatform for a few days, and I struggle to find the correct resources to learn about theming for Compose Multiplatform. Here is how I do it—it’s not perfect, and it’s not the only solution, but it’s mine.</p>
<h1 id="heading-gradle-setup">Gradle setup</h1>
<p>First, make sure you have the correct dependencies in your <code>composeApp/build.gradle.kts</code>. I made this mistake and lost a good hour. If you have <code>implementation(compose.material)</code> instead of <code>implementation(compose.material3)</code>, just add the number 3 and sync.</p>
<pre><code class="lang-kotlin">    sourceSets {

        androidMain.dependencies {
            implementation(compose.preview)
            implementation(libs.androidx.activity.compose)
            implementation(project.dependencies.platform(libs.firebase.bom))
            implementation(libs.firebase.common.ktx)
            implementation(libs.koin.android)
            implementation(libs.koin.androidx.compose)
        }
        commonMain.dependencies {
            implementation(compose.runtime)
            implementation(compose.foundation)
            implementation(compose.material3) <span class="hljs-comment">// &lt;-- Make sure it is Material 3 and not Material 2</span>
            implementation(compose.ui)
            implementation(compose.components.resources)
            implementation(compose.components.uiToolingPreview)
            ...
        }
    }
</code></pre>
<h1 id="heading-choose-a-theme">Choose a theme</h1>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734434244260/10cee40a-41c5-42e2-a1d6-0aae4ed13d33.png" alt="Screen shot of Material Theme Builder" class="image--center mx-auto" /></p>
<p>For that, you can use the Material 3 theme builder to select your colors and fonts. I won’t elaborate on this because it’s pretty straightforward. Just make sure that, in the end, you download the “Jetpack Compose Theme.”</p>
<p>You should end up with something like this :</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734434332861/bf9e7b12-9ac6-4a7a-ab74-f5c44c4be379.png" alt="Screen shot of the Finder showing the file structure of the file you get when you download the Jetpack Compose theme from the Theme Builder" class="image--center mx-auto" /></p>
<h1 id="heading-setup-the-fonts">Setup the fonts</h1>
<p>Now, it’s time to set up the fonts in your Compose resources. First, create a folder named <code>font</code> in the <code>composeResources</code> folder. Make sure to call it <code>font</code> and not <code>fonts</code>. Previously, you selected your fonts using your theme. However, we will not use the file generated by the theme builder because you cannot use the GoogleFonts library in your <code>commonMain</code>. In order to be able to use those resources, you need to build your project.</p>
<p>Instead, you will need to go to the <a target="_blank" href="https://fonts.google.com/">Google Fonts</a> website or any website where you can download the resources in <code>.ttf</code> format to use your fonts in the app. Then, paste the <code>.ttf</code> files that you need (or paste everything and clean up once you are done building).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734435879472/1cd95c26-83d0-4a31-bc6d-83e15e80cbbe.png" alt="Screeshot of the project structure of an Android app, it shows where the .ttf files have been put. " class="image--center mx-auto" /></p>
<h1 id="heading-setup-the-theme">Setup the theme</h1>
<p>In the <code>composeApp/src/commonMain/kotlin/com/example/yourappname</code> directory, paste the <code>theme</code> folder. The exact location depends on how you organize your project. Note that the <code>Padding</code> file was added by me, so there’s no need to look for it.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734434651651/a01acb02-36ab-4e29-9d71-bf7e7e94710f.png" alt="Screenshot of the Android file structure showing where I pasted the theme folder from the Material theme builder" class="image--center mx-auto" /></p>
<h2 id="heading-setup-the-colorkt-file">Setup the Color.kt file</h2>
<p>Simple, just paste it. It should work without needing any alterations.</p>
<h2 id="heading-setup-the-typekt-file">Setup the Type.kt file</h2>
<p>Here is the initial Type.kt that I get :</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">package</span> com.example.ui.theme

<span class="hljs-keyword">import</span> androidx.compose.material3.Typography
<span class="hljs-keyword">import</span> androidx.compose.ui.text.TextStyle
<span class="hljs-keyword">import</span> androidx.compose.ui.text.font.FontFamily
<span class="hljs-keyword">import</span> androidx.compose.ui.text.font.FontWeight
<span class="hljs-keyword">import</span> androidx.compose.ui.unit.sp

<span class="hljs-keyword">import</span> androidx.compose.ui.text.googlefonts.GoogleFont
<span class="hljs-keyword">import</span> androidx.compose.ui.text.googlefonts.Font

<span class="hljs-keyword">val</span> provider = GoogleFont.Provider(
    providerAuthority = <span class="hljs-string">"com.google.android.gms.fonts"</span>,
    providerPackage = <span class="hljs-string">"com.google.android.gms"</span>,
    certificates = R.array.com_google_android_gms_fonts_certs
)

<span class="hljs-keyword">val</span> bodyFontFamily = FontFamily(
    Font(
        googleFont = GoogleFont(<span class="hljs-string">"Roboto"</span>),
        fontProvider = provider,
    )
)

<span class="hljs-keyword">val</span> displayFontFamily = FontFamily(
    Font(
        googleFont = GoogleFont(<span class="hljs-string">"Merriweather"</span>),
        fontProvider = provider,
    )
)

<span class="hljs-comment">// Default Material 3 typography values</span>
<span class="hljs-keyword">val</span> baseline = Typography()

<span class="hljs-keyword">val</span> AppTypography = Typography(
    displayLarge = baseline.displayLarge.copy(fontFamily = displayFontFamily),
    displayMedium = baseline.displayMedium.copy(fontFamily = displayFontFamily),
    displaySmall = baseline.displaySmall.copy(fontFamily = displayFontFamily),
    headlineLarge = baseline.headlineLarge.copy(fontFamily = displayFontFamily),
    headlineMedium = baseline.headlineMedium.copy(fontFamily = displayFontFamily),
    headlineSmall = baseline.headlineSmall.copy(fontFamily = displayFontFamily),
    titleLarge = baseline.titleLarge.copy(fontFamily = displayFontFamily),
    titleMedium = baseline.titleMedium.copy(fontFamily = displayFontFamily),
    titleSmall = baseline.titleSmall.copy(fontFamily = displayFontFamily),
    bodyLarge = baseline.bodyLarge.copy(fontFamily = bodyFontFamily),
    bodyMedium = baseline.bodyMedium.copy(fontFamily = bodyFontFamily),
    bodySmall = baseline.bodySmall.copy(fontFamily = bodyFontFamily),
    labelLarge = baseline.labelLarge.copy(fontFamily = bodyFontFamily),
    labelMedium = baseline.labelMedium.copy(fontFamily = bodyFontFamily),
    labelSmall = baseline.labelSmall.copy(fontFamily = bodyFontFamily),
)
</code></pre>
<p>Here is what I do with it because we cannot use GoogleFonts :</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">package</span> com.alphaomardiallo.perspective.common.theme

<span class="hljs-keyword">import</span> androidx.compose.material3.Typography
<span class="hljs-keyword">import</span> androidx.compose.runtime.Composable
<span class="hljs-keyword">import</span> androidx.compose.ui.text.font.FontFamily
<span class="hljs-keyword">import</span> androidx.compose.ui.text.font.FontWeight
<span class="hljs-keyword">import</span> org.jetbrains.compose.resources.Font
<span class="hljs-keyword">import</span> perspective.composeapp.generated.resources.*


<span class="hljs-keyword">val</span> baseline = Typography()

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">RobotoFontFamily</span><span class="hljs-params">()</span></span> = FontFamily(
    Font(Res.font.Roboto_Regular, FontWeight.Normal),
    Font(Res.font.Roboto_Bold, FontWeight.Bold),
    Font(Res.font.Roboto_Light, FontWeight.Light),
    Font(Res.font.Roboto_Medium, FontWeight.Medium),
    Font(Res.font.Roboto_Thin, FontWeight.Thin)
)

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">MerriweatherFontFamily</span><span class="hljs-params">()</span></span> = FontFamily(
    Font(Res.font.Merriweather_Regular, FontWeight.Normal),
    Font(Res.font.Merriweather_Bold, FontWeight.Bold),
    Font(Res.font.Merriweather_Light, FontWeight.Light)
)

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">AppTypography</span><span class="hljs-params">()</span></span> = Typography().run {
    <span class="hljs-keyword">val</span> merriweatherFontFamily = MerriweatherFontFamily()
    <span class="hljs-keyword">val</span> robotoFontFamily = RobotoFontFamily()
    copy(
        displayLarge = baseline.displayLarge.copy(fontFamily = merriweatherFontFamily),
        displayMedium = baseline.displayMedium.copy(fontFamily = merriweatherFontFamily),
        displaySmall = baseline.displaySmall.copy(fontFamily = merriweatherFontFamily),
        headlineLarge = baseline.headlineLarge.copy(fontFamily = merriweatherFontFamily),
        headlineMedium = baseline.headlineMedium.copy(fontFamily = merriweatherFontFamily),
        headlineSmall = baseline.headlineSmall.copy(fontFamily = merriweatherFontFamily),
        titleLarge = baseline.titleLarge.copy(fontFamily = merriweatherFontFamily),
        titleMedium = baseline.titleMedium.copy(fontFamily = merriweatherFontFamily),
        titleSmall = baseline.titleSmall.copy(fontFamily = merriweatherFontFamily),
        bodyLarge = baseline.bodyLarge.copy(fontFamily = robotoFontFamily),
        bodyMedium = baseline.bodyMedium.copy(fontFamily = robotoFontFamily),
        bodySmall = baseline.bodySmall.copy(fontFamily = robotoFontFamily),
        labelLarge = baseline.labelLarge.copy(fontFamily = robotoFontFamily),
        labelMedium = baseline.labelMedium.copy(fontFamily = robotoFontFamily),
        labelSmall = baseline.labelSmall.copy(fontFamily = robotoFontFamily)
    )
}
</code></pre>
<p>If you want more details on why, please check out <a target="_blank" href="https://medium.com/@boobalaninfo/integrate-custom-fonts-in-kotlin-compose-multiplatform-bc0f30c0b7e6">this article</a> written by <strong>Boobalan Munusamy</strong>. I literally followed his guide step by step.</p>
<h2 id="heading-setup-the-themekt-file">Setup the Theme.kt file</h2>
<p>Here is the Theme.kt file that I get from the Theme Builder :</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">package</span> com.example.compose
<span class="hljs-keyword">import</span> android.app.Activity
<span class="hljs-keyword">import</span> android.os.Build
<span class="hljs-keyword">import</span> androidx.compose.foundation.isSystemInDarkTheme
<span class="hljs-keyword">import</span> androidx.compose.material3.MaterialTheme
<span class="hljs-keyword">import</span> androidx.compose.material3.lightColorScheme
<span class="hljs-keyword">import</span> androidx.compose.material3.darkColorScheme
<span class="hljs-keyword">import</span> androidx.compose.material3.dynamicDarkColorScheme
<span class="hljs-keyword">import</span> androidx.compose.material3.dynamicLightColorScheme
<span class="hljs-keyword">import</span> androidx.compose.material3.Typography
<span class="hljs-keyword">import</span> androidx.compose.runtime.Composable
<span class="hljs-keyword">import</span> androidx.compose.runtime.Immutable
<span class="hljs-keyword">import</span> androidx.compose.ui.graphics.Color
<span class="hljs-keyword">import</span> androidx.compose.ui.graphics.toArgb
<span class="hljs-keyword">import</span> androidx.compose.ui.platform.LocalContext

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> lightScheme = lightColorScheme(
    primary = primaryLight,
    onPrimary = onPrimaryLight,
    primaryContainer = primaryContainerLight,
    onPrimaryContainer = onPrimaryContainerLight,
    secondary = secondaryLight,
    onSecondary = onSecondaryLight,
    secondaryContainer = secondaryContainerLight,
    onSecondaryContainer = onSecondaryContainerLight,
    tertiary = tertiaryLight,
    onTertiary = onTertiaryLight,
    tertiaryContainer = tertiaryContainerLight,
    onTertiaryContainer = onTertiaryContainerLight,
    error = errorLight,
    onError = onErrorLight,
    errorContainer = errorContainerLight,
    onErrorContainer = onErrorContainerLight,
    background = backgroundLight,
    onBackground = onBackgroundLight,
    surface = surfaceLight,
    onSurface = onSurfaceLight,
    surfaceVariant = surfaceVariantLight,
    onSurfaceVariant = onSurfaceVariantLight,
    outline = outlineLight,
    outlineVariant = outlineVariantLight,
    scrim = scrimLight,
    inverseSurface = inverseSurfaceLight,
    inverseOnSurface = inverseOnSurfaceLight,
    inversePrimary = inversePrimaryLight,
    surfaceDim = surfaceDimLight,
    surfaceBright = surfaceBrightLight,
    surfaceContainerLowest = surfaceContainerLowestLight,
    surfaceContainerLow = surfaceContainerLowLight,
    surfaceContainer = surfaceContainerLight,
    surfaceContainerHigh = surfaceContainerHighLight,
    surfaceContainerHighest = surfaceContainerHighestLight,
)

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> darkScheme = darkColorScheme(
    primary = primaryDark,
    onPrimary = onPrimaryDark,
    primaryContainer = primaryContainerDark,
    onPrimaryContainer = onPrimaryContainerDark,
    secondary = secondaryDark,
    onSecondary = onSecondaryDark,
    secondaryContainer = secondaryContainerDark,
    onSecondaryContainer = onSecondaryContainerDark,
    tertiary = tertiaryDark,
    onTertiary = onTertiaryDark,
    tertiaryContainer = tertiaryContainerDark,
    onTertiaryContainer = onTertiaryContainerDark,
    error = errorDark,
    onError = onErrorDark,
    errorContainer = errorContainerDark,
    onErrorContainer = onErrorContainerDark,
    background = backgroundDark,
    onBackground = onBackgroundDark,
    surface = surfaceDark,
    onSurface = onSurfaceDark,
    surfaceVariant = surfaceVariantDark,
    onSurfaceVariant = onSurfaceVariantDark,
    outline = outlineDark,
    outlineVariant = outlineVariantDark,
    scrim = scrimDark,
    inverseSurface = inverseSurfaceDark,
    inverseOnSurface = inverseOnSurfaceDark,
    inversePrimary = inversePrimaryDark,
    surfaceDim = surfaceDimDark,
    surfaceBright = surfaceBrightDark,
    surfaceContainerLowest = surfaceContainerLowestDark,
    surfaceContainerLow = surfaceContainerLowDark,
    surfaceContainer = surfaceContainerDark,
    surfaceContainerHigh = surfaceContainerHighDark,
    surfaceContainerHighest = surfaceContainerHighestDark,
)

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> mediumContrastLightColorScheme = lightColorScheme(
    primary = primaryLightMediumContrast,
    onPrimary = onPrimaryLightMediumContrast,
    primaryContainer = primaryContainerLightMediumContrast,
    onPrimaryContainer = onPrimaryContainerLightMediumContrast,
    secondary = secondaryLightMediumContrast,
    onSecondary = onSecondaryLightMediumContrast,
    secondaryContainer = secondaryContainerLightMediumContrast,
    onSecondaryContainer = onSecondaryContainerLightMediumContrast,
    tertiary = tertiaryLightMediumContrast,
    onTertiary = onTertiaryLightMediumContrast,
    tertiaryContainer = tertiaryContainerLightMediumContrast,
    onTertiaryContainer = onTertiaryContainerLightMediumContrast,
    error = errorLightMediumContrast,
    onError = onErrorLightMediumContrast,
    errorContainer = errorContainerLightMediumContrast,
    onErrorContainer = onErrorContainerLightMediumContrast,
    background = backgroundLightMediumContrast,
    onBackground = onBackgroundLightMediumContrast,
    surface = surfaceLightMediumContrast,
    onSurface = onSurfaceLightMediumContrast,
    surfaceVariant = surfaceVariantLightMediumContrast,
    onSurfaceVariant = onSurfaceVariantLightMediumContrast,
    outline = outlineLightMediumContrast,
    outlineVariant = outlineVariantLightMediumContrast,
    scrim = scrimLightMediumContrast,
    inverseSurface = inverseSurfaceLightMediumContrast,
    inverseOnSurface = inverseOnSurfaceLightMediumContrast,
    inversePrimary = inversePrimaryLightMediumContrast,
    surfaceDim = surfaceDimLightMediumContrast,
    surfaceBright = surfaceBrightLightMediumContrast,
    surfaceContainerLowest = surfaceContainerLowestLightMediumContrast,
    surfaceContainerLow = surfaceContainerLowLightMediumContrast,
    surfaceContainer = surfaceContainerLightMediumContrast,
    surfaceContainerHigh = surfaceContainerHighLightMediumContrast,
    surfaceContainerHighest = surfaceContainerHighestLightMediumContrast,
)

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> highContrastLightColorScheme = lightColorScheme(
    primary = primaryLightHighContrast,
    onPrimary = onPrimaryLightHighContrast,
    primaryContainer = primaryContainerLightHighContrast,
    onPrimaryContainer = onPrimaryContainerLightHighContrast,
    secondary = secondaryLightHighContrast,
    onSecondary = onSecondaryLightHighContrast,
    secondaryContainer = secondaryContainerLightHighContrast,
    onSecondaryContainer = onSecondaryContainerLightHighContrast,
    tertiary = tertiaryLightHighContrast,
    onTertiary = onTertiaryLightHighContrast,
    tertiaryContainer = tertiaryContainerLightHighContrast,
    onTertiaryContainer = onTertiaryContainerLightHighContrast,
    error = errorLightHighContrast,
    onError = onErrorLightHighContrast,
    errorContainer = errorContainerLightHighContrast,
    onErrorContainer = onErrorContainerLightHighContrast,
    background = backgroundLightHighContrast,
    onBackground = onBackgroundLightHighContrast,
    surface = surfaceLightHighContrast,
    onSurface = onSurfaceLightHighContrast,
    surfaceVariant = surfaceVariantLightHighContrast,
    onSurfaceVariant = onSurfaceVariantLightHighContrast,
    outline = outlineLightHighContrast,
    outlineVariant = outlineVariantLightHighContrast,
    scrim = scrimLightHighContrast,
    inverseSurface = inverseSurfaceLightHighContrast,
    inverseOnSurface = inverseOnSurfaceLightHighContrast,
    inversePrimary = inversePrimaryLightHighContrast,
    surfaceDim = surfaceDimLightHighContrast,
    surfaceBright = surfaceBrightLightHighContrast,
    surfaceContainerLowest = surfaceContainerLowestLightHighContrast,
    surfaceContainerLow = surfaceContainerLowLightHighContrast,
    surfaceContainer = surfaceContainerLightHighContrast,
    surfaceContainerHigh = surfaceContainerHighLightHighContrast,
    surfaceContainerHighest = surfaceContainerHighestLightHighContrast,
)

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> mediumContrastDarkColorScheme = darkColorScheme(
    primary = primaryDarkMediumContrast,
    onPrimary = onPrimaryDarkMediumContrast,
    primaryContainer = primaryContainerDarkMediumContrast,
    onPrimaryContainer = onPrimaryContainerDarkMediumContrast,
    secondary = secondaryDarkMediumContrast,
    onSecondary = onSecondaryDarkMediumContrast,
    secondaryContainer = secondaryContainerDarkMediumContrast,
    onSecondaryContainer = onSecondaryContainerDarkMediumContrast,
    tertiary = tertiaryDarkMediumContrast,
    onTertiary = onTertiaryDarkMediumContrast,
    tertiaryContainer = tertiaryContainerDarkMediumContrast,
    onTertiaryContainer = onTertiaryContainerDarkMediumContrast,
    error = errorDarkMediumContrast,
    onError = onErrorDarkMediumContrast,
    errorContainer = errorContainerDarkMediumContrast,
    onErrorContainer = onErrorContainerDarkMediumContrast,
    background = backgroundDarkMediumContrast,
    onBackground = onBackgroundDarkMediumContrast,
    surface = surfaceDarkMediumContrast,
    onSurface = onSurfaceDarkMediumContrast,
    surfaceVariant = surfaceVariantDarkMediumContrast,
    onSurfaceVariant = onSurfaceVariantDarkMediumContrast,
    outline = outlineDarkMediumContrast,
    outlineVariant = outlineVariantDarkMediumContrast,
    scrim = scrimDarkMediumContrast,
    inverseSurface = inverseSurfaceDarkMediumContrast,
    inverseOnSurface = inverseOnSurfaceDarkMediumContrast,
    inversePrimary = inversePrimaryDarkMediumContrast,
    surfaceDim = surfaceDimDarkMediumContrast,
    surfaceBright = surfaceBrightDarkMediumContrast,
    surfaceContainerLowest = surfaceContainerLowestDarkMediumContrast,
    surfaceContainerLow = surfaceContainerLowDarkMediumContrast,
    surfaceContainer = surfaceContainerDarkMediumContrast,
    surfaceContainerHigh = surfaceContainerHighDarkMediumContrast,
    surfaceContainerHighest = surfaceContainerHighestDarkMediumContrast,
)

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> highContrastDarkColorScheme = darkColorScheme(
    primary = primaryDarkHighContrast,
    onPrimary = onPrimaryDarkHighContrast,
    primaryContainer = primaryContainerDarkHighContrast,
    onPrimaryContainer = onPrimaryContainerDarkHighContrast,
    secondary = secondaryDarkHighContrast,
    onSecondary = onSecondaryDarkHighContrast,
    secondaryContainer = secondaryContainerDarkHighContrast,
    onSecondaryContainer = onSecondaryContainerDarkHighContrast,
    tertiary = tertiaryDarkHighContrast,
    onTertiary = onTertiaryDarkHighContrast,
    tertiaryContainer = tertiaryContainerDarkHighContrast,
    onTertiaryContainer = onTertiaryContainerDarkHighContrast,
    error = errorDarkHighContrast,
    onError = onErrorDarkHighContrast,
    errorContainer = errorContainerDarkHighContrast,
    onErrorContainer = onErrorContainerDarkHighContrast,
    background = backgroundDarkHighContrast,
    onBackground = onBackgroundDarkHighContrast,
    surface = surfaceDarkHighContrast,
    onSurface = onSurfaceDarkHighContrast,
    surfaceVariant = surfaceVariantDarkHighContrast,
    onSurfaceVariant = onSurfaceVariantDarkHighContrast,
    outline = outlineDarkHighContrast,
    outlineVariant = outlineVariantDarkHighContrast,
    scrim = scrimDarkHighContrast,
    inverseSurface = inverseSurfaceDarkHighContrast,
    inverseOnSurface = inverseOnSurfaceDarkHighContrast,
    inversePrimary = inversePrimaryDarkHighContrast,
    surfaceDim = surfaceDimDarkHighContrast,
    surfaceBright = surfaceBrightDarkHighContrast,
    surfaceContainerLowest = surfaceContainerLowestDarkHighContrast,
    surfaceContainerLow = surfaceContainerLowDarkHighContrast,
    surfaceContainer = surfaceContainerDarkHighContrast,
    surfaceContainerHigh = surfaceContainerHighDarkHighContrast,
    surfaceContainerHighest = surfaceContainerHighestDarkHighContrast,
)

<span class="hljs-keyword">val</span> unspecified_scheme = ColorFamily(
    Color.Unspecified, Color.Unspecified, Color.Unspecified, Color.Unspecified
)

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">AppTheme</span><span class="hljs-params">(
    darkTheme: <span class="hljs-type">Boolean</span> = isSystemInDarkTheme()</span></span>,
    <span class="hljs-comment">// Dynamic color is available on Android 12+</span>
    dynamicColor: <span class="hljs-built_in">Boolean</span> = <span class="hljs-literal">true</span>,
    content: <span class="hljs-meta">@Composable()</span> () -&gt; <span class="hljs-built_in">Unit</span>
) {
  <span class="hljs-keyword">val</span> colorScheme = <span class="hljs-keyword">when</span> {
      dynamicColor &amp;&amp; Build.VERSION.SDK_INT &gt;= Build.VERSION_CODES.S -&gt; {
          <span class="hljs-keyword">val</span> context = LocalContext.current
          <span class="hljs-keyword">if</span> (darkTheme) dynamicDarkColorScheme(context) <span class="hljs-keyword">else</span> dynamicLightColorScheme(context)
      }

      darkTheme -&gt; darkScheme
      <span class="hljs-keyword">else</span> -&gt; lightScheme
  }

  MaterialTheme(
    colorScheme = colorScheme,
    typography = AppTypography,
    content = content
  )
}
</code></pre>
<p>And here is what I do with it, because I do not use the dynamic color option (I hate it, but it’s my take) and because I believe accessibility is important :</p>
<pre><code class="lang-kotlin"><span class="hljs-keyword">package</span> com.alphaomardiallo.perspective.common.theme

<span class="hljs-keyword">import</span> androidx.compose.foundation.isSystemInDarkTheme
<span class="hljs-keyword">import</span> androidx.compose.material3.MaterialTheme
<span class="hljs-keyword">import</span> androidx.compose.material3.darkColorScheme
<span class="hljs-keyword">import</span> androidx.compose.material3.lightColorScheme
<span class="hljs-keyword">import</span> androidx.compose.runtime.Composable
<span class="hljs-keyword">import</span> androidx.compose.runtime.Immutable
<span class="hljs-keyword">import</span> androidx.compose.ui.graphics.Color

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> lightScheme = lightColorScheme(
    primary = primaryLight,
    onPrimary = onPrimaryLight,
    primaryContainer = primaryContainerLight,
    onPrimaryContainer = onPrimaryContainerLight,
    secondary = secondaryLight,
    onSecondary = onSecondaryLight,
    secondaryContainer = secondaryContainerLight,
    onSecondaryContainer = onSecondaryContainerLight,
    tertiary = tertiaryLight,
    onTertiary = onTertiaryLight,
    tertiaryContainer = tertiaryContainerLight,
    onTertiaryContainer = onTertiaryContainerLight,
    error = errorLight,
    onError = onErrorLight,
    errorContainer = errorContainerLight,
    onErrorContainer = onErrorContainerLight,
    background = backgroundLight,
    onBackground = onBackgroundLight,
    surface = surfaceLight,
    onSurface = onSurfaceLight,
    surfaceVariant = surfaceVariantLight,
    onSurfaceVariant = onSurfaceVariantLight,
    outline = outlineLight,
    outlineVariant = outlineVariantLight,
    scrim = scrimLight,
    inverseSurface = inverseSurfaceLight,
    inverseOnSurface = inverseOnSurfaceLight,
    inversePrimary = inversePrimaryLight,
    surfaceDim = surfaceDimLight,
    surfaceBright = surfaceBrightLight,
    surfaceContainerLowest = surfaceContainerLowestLight,
    surfaceContainerLow = surfaceContainerLowLight,
    surfaceContainer = surfaceContainerLight,
    surfaceContainerHigh = surfaceContainerHighLight,
    surfaceContainerHighest = surfaceContainerHighestLight,
)

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> darkScheme = darkColorScheme(
    primary = primaryDark,
    onPrimary = onPrimaryDark,
    primaryContainer = primaryContainerDark,
    onPrimaryContainer = onPrimaryContainerDark,
    secondary = secondaryDark,
    onSecondary = onSecondaryDark,
    secondaryContainer = secondaryContainerDark,
    onSecondaryContainer = onSecondaryContainerDark,
    tertiary = tertiaryDark,
    onTertiary = onTertiaryDark,
    tertiaryContainer = tertiaryContainerDark,
    onTertiaryContainer = onTertiaryContainerDark,
    error = errorDark,
    onError = onErrorDark,
    errorContainer = errorContainerDark,
    onErrorContainer = onErrorContainerDark,
    background = backgroundDark,
    onBackground = onBackgroundDark,
    surface = surfaceDark,
    onSurface = onSurfaceDark,
    surfaceVariant = surfaceVariantDark,
    onSurfaceVariant = onSurfaceVariantDark,
    outline = outlineDark,
    outlineVariant = outlineVariantDark,
    scrim = scrimDark,
    inverseSurface = inverseSurfaceDark,
    inverseOnSurface = inverseOnSurfaceDark,
    inversePrimary = inversePrimaryDark,
    surfaceDim = surfaceDimDark,
    surfaceBright = surfaceBrightDark,
    surfaceContainerLowest = surfaceContainerLowestDark,
    surfaceContainerLow = surfaceContainerLowDark,
    surfaceContainer = surfaceContainerDark,
    surfaceContainerHigh = surfaceContainerHighDark,
    surfaceContainerHighest = surfaceContainerHighestDark,
)

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> mediumContrastLightColorScheme = lightColorScheme(
    primary = primaryLightMediumContrast,
    onPrimary = onPrimaryLightMediumContrast,
    primaryContainer = primaryContainerLightMediumContrast,
    onPrimaryContainer = onPrimaryContainerLightMediumContrast,
    secondary = secondaryLightMediumContrast,
    onSecondary = onSecondaryLightMediumContrast,
    secondaryContainer = secondaryContainerLightMediumContrast,
    onSecondaryContainer = onSecondaryContainerLightMediumContrast,
    tertiary = tertiaryLightMediumContrast,
    onTertiary = onTertiaryLightMediumContrast,
    tertiaryContainer = tertiaryContainerLightMediumContrast,
    onTertiaryContainer = onTertiaryContainerLightMediumContrast,
    error = errorLightMediumContrast,
    onError = onErrorLightMediumContrast,
    errorContainer = errorContainerLightMediumContrast,
    onErrorContainer = onErrorContainerLightMediumContrast,
    background = backgroundLightMediumContrast,
    onBackground = onBackgroundLightMediumContrast,
    surface = surfaceLightMediumContrast,
    onSurface = onSurfaceLightMediumContrast,
    surfaceVariant = surfaceVariantLightMediumContrast,
    onSurfaceVariant = onSurfaceVariantLightMediumContrast,
    outline = outlineLightMediumContrast,
    outlineVariant = outlineVariantLightMediumContrast,
    scrim = scrimLightMediumContrast,
    inverseSurface = inverseSurfaceLightMediumContrast,
    inverseOnSurface = inverseOnSurfaceLightMediumContrast,
    inversePrimary = inversePrimaryLightMediumContrast,
    surfaceDim = surfaceDimLightMediumContrast,
    surfaceBright = surfaceBrightLightMediumContrast,
    surfaceContainerLowest = surfaceContainerLowestLightMediumContrast,
    surfaceContainerLow = surfaceContainerLowLightMediumContrast,
    surfaceContainer = surfaceContainerLightMediumContrast,
    surfaceContainerHigh = surfaceContainerHighLightMediumContrast,
    surfaceContainerHighest = surfaceContainerHighestLightMediumContrast,
)

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> highContrastLightColorScheme = lightColorScheme(
    primary = primaryLightHighContrast,
    onPrimary = onPrimaryLightHighContrast,
    primaryContainer = primaryContainerLightHighContrast,
    onPrimaryContainer = onPrimaryContainerLightHighContrast,
    secondary = secondaryLightHighContrast,
    onSecondary = onSecondaryLightHighContrast,
    secondaryContainer = secondaryContainerLightHighContrast,
    onSecondaryContainer = onSecondaryContainerLightHighContrast,
    tertiary = tertiaryLightHighContrast,
    onTertiary = onTertiaryLightHighContrast,
    tertiaryContainer = tertiaryContainerLightHighContrast,
    onTertiaryContainer = onTertiaryContainerLightHighContrast,
    error = errorLightHighContrast,
    onError = onErrorLightHighContrast,
    errorContainer = errorContainerLightHighContrast,
    onErrorContainer = onErrorContainerLightHighContrast,
    background = backgroundLightHighContrast,
    onBackground = onBackgroundLightHighContrast,
    surface = surfaceLightHighContrast,
    onSurface = onSurfaceLightHighContrast,
    surfaceVariant = surfaceVariantLightHighContrast,
    onSurfaceVariant = onSurfaceVariantLightHighContrast,
    outline = outlineLightHighContrast,
    outlineVariant = outlineVariantLightHighContrast,
    scrim = scrimLightHighContrast,
    inverseSurface = inverseSurfaceLightHighContrast,
    inverseOnSurface = inverseOnSurfaceLightHighContrast,
    inversePrimary = inversePrimaryLightHighContrast,
    surfaceDim = surfaceDimLightHighContrast,
    surfaceBright = surfaceBrightLightHighContrast,
    surfaceContainerLowest = surfaceContainerLowestLightHighContrast,
    surfaceContainerLow = surfaceContainerLowLightHighContrast,
    surfaceContainer = surfaceContainerLightHighContrast,
    surfaceContainerHigh = surfaceContainerHighLightHighContrast,
    surfaceContainerHighest = surfaceContainerHighestLightHighContrast,
)

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> mediumContrastDarkColorScheme = darkColorScheme(
    primary = primaryDarkMediumContrast,
    onPrimary = onPrimaryDarkMediumContrast,
    primaryContainer = primaryContainerDarkMediumContrast,
    onPrimaryContainer = onPrimaryContainerDarkMediumContrast,
    secondary = secondaryDarkMediumContrast,
    onSecondary = onSecondaryDarkMediumContrast,
    secondaryContainer = secondaryContainerDarkMediumContrast,
    onSecondaryContainer = onSecondaryContainerDarkMediumContrast,
    tertiary = tertiaryDarkMediumContrast,
    onTertiary = onTertiaryDarkMediumContrast,
    tertiaryContainer = tertiaryContainerDarkMediumContrast,
    onTertiaryContainer = onTertiaryContainerDarkMediumContrast,
    error = errorDarkMediumContrast,
    onError = onErrorDarkMediumContrast,
    errorContainer = errorContainerDarkMediumContrast,
    onErrorContainer = onErrorContainerDarkMediumContrast,
    background = backgroundDarkMediumContrast,
    onBackground = onBackgroundDarkMediumContrast,
    surface = surfaceDarkMediumContrast,
    onSurface = onSurfaceDarkMediumContrast,
    surfaceVariant = surfaceVariantDarkMediumContrast,
    onSurfaceVariant = onSurfaceVariantDarkMediumContrast,
    outline = outlineDarkMediumContrast,
    outlineVariant = outlineVariantDarkMediumContrast,
    scrim = scrimDarkMediumContrast,
    inverseSurface = inverseSurfaceDarkMediumContrast,
    inverseOnSurface = inverseOnSurfaceDarkMediumContrast,
    inversePrimary = inversePrimaryDarkMediumContrast,
    surfaceDim = surfaceDimDarkMediumContrast,
    surfaceBright = surfaceBrightDarkMediumContrast,
    surfaceContainerLowest = surfaceContainerLowestDarkMediumContrast,
    surfaceContainerLow = surfaceContainerLowDarkMediumContrast,
    surfaceContainer = surfaceContainerDarkMediumContrast,
    surfaceContainerHigh = surfaceContainerHighDarkMediumContrast,
    surfaceContainerHighest = surfaceContainerHighestDarkMediumContrast,
)

<span class="hljs-keyword">private</span> <span class="hljs-keyword">val</span> highContrastDarkColorScheme = darkColorScheme(
    primary = primaryDarkHighContrast,
    onPrimary = onPrimaryDarkHighContrast,
    primaryContainer = primaryContainerDarkHighContrast,
    onPrimaryContainer = onPrimaryContainerDarkHighContrast,
    secondary = secondaryDarkHighContrast,
    onSecondary = onSecondaryDarkHighContrast,
    secondaryContainer = secondaryContainerDarkHighContrast,
    onSecondaryContainer = onSecondaryContainerDarkHighContrast,
    tertiary = tertiaryDarkHighContrast,
    onTertiary = onTertiaryDarkHighContrast,
    tertiaryContainer = tertiaryContainerDarkHighContrast,
    onTertiaryContainer = onTertiaryContainerDarkHighContrast,
    error = errorDarkHighContrast,
    onError = onErrorDarkHighContrast,
    errorContainer = errorContainerDarkHighContrast,
    onErrorContainer = onErrorContainerDarkHighContrast,
    background = backgroundDarkHighContrast,
    onBackground = onBackgroundDarkHighContrast,
    surface = surfaceDarkHighContrast,
    onSurface = onSurfaceDarkHighContrast,
    surfaceVariant = surfaceVariantDarkHighContrast,
    onSurfaceVariant = onSurfaceVariantDarkHighContrast,
    outline = outlineDarkHighContrast,
    outlineVariant = outlineVariantDarkHighContrast,
    scrim = scrimDarkHighContrast,
    inverseSurface = inverseSurfaceDarkHighContrast,
    inverseOnSurface = inverseOnSurfaceDarkHighContrast,
    inversePrimary = inversePrimaryDarkHighContrast,
    surfaceDim = surfaceDimDarkHighContrast,
    surfaceBright = surfaceBrightDarkHighContrast,
    surfaceContainerLowest = surfaceContainerLowestDarkHighContrast,
    surfaceContainerLow = surfaceContainerLowDarkHighContrast,
    surfaceContainer = surfaceContainerDarkHighContrast,
    surfaceContainerHigh = surfaceContainerHighDarkHighContrast,
    surfaceContainerHighest = surfaceContainerHighestDarkHighContrast,
)

<span class="hljs-meta">@Immutable</span>
<span class="hljs-keyword">data</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ColorFamily</span></span>(
    <span class="hljs-keyword">val</span> color: Color,
    <span class="hljs-keyword">val</span> onColor: Color,
    <span class="hljs-keyword">val</span> colorContainer: Color,
    <span class="hljs-keyword">val</span> onColorContainer: Color
)

<span class="hljs-meta">@Composable</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">AppTheme</span><span class="hljs-params">(
    darkTheme: <span class="hljs-type">Boolean</span> = isSystemInDarkTheme()</span></span>,
    highContrast: <span class="hljs-built_in">Boolean</span> = <span class="hljs-literal">false</span>,
    mediumContrast: <span class="hljs-built_in">Boolean</span> = <span class="hljs-literal">false</span>,
    content: <span class="hljs-meta">@Composable()</span> () -&gt; <span class="hljs-built_in">Unit</span>
) {
    <span class="hljs-keyword">val</span> colorScheme = <span class="hljs-keyword">when</span> {
        highContrast -&gt; {
            <span class="hljs-keyword">if</span> (darkTheme) highContrastDarkColorScheme <span class="hljs-keyword">else</span> highContrastLightColorScheme
        }

        mediumContrast -&gt; {
            <span class="hljs-keyword">if</span> (darkTheme) mediumContrastDarkColorScheme <span class="hljs-keyword">else</span> mediumContrastLightColorScheme
        }

        darkTheme -&gt; darkScheme
        <span class="hljs-keyword">else</span> -&gt; lightScheme
    }

    MaterialTheme(
        colorScheme = colorScheme,
        typography = AppTypography(),
        content = content
    )
}
</code></pre>
<h1 id="heading-apply-your-theme">Apply your theme</h1>
<p>Now that this is done, you need to apply it to your app and check that it works. Open the App.kt file and do this :</p>
<pre><code class="lang-kotlin"><span class="hljs-meta">@Composable</span>
<span class="hljs-meta">@Preview</span>
<span class="hljs-function"><span class="hljs-keyword">fun</span> <span class="hljs-title">App</span><span class="hljs-params">(viewModel: <span class="hljs-type">AppViewModel</span> = koinViewModel()</span></span>) {
    <span class="hljs-keyword">val</span> navController = rememberNavController()
    <span class="hljs-keyword">val</span> currentRoute = remember { mutableStateOf(BottomNavItem.Feed.route) }
    <span class="hljs-comment">//val state = viewModel.uiState.collectAsStateWithLifecycle()</span>

    AppTheme { <span class="hljs-comment">// &lt;- Replace MaterialTheme by AppTheme or whatever name you chose</span>
        Scaffold(
            modifier = Modifier.fillMaxSize(),
            topBar = { },
            bottomBar = {
                NavigationBar(modifier = Modifier.fillMaxWidth()) {
                    listOf(
                        BottomNavItem.Feed,
                        BottomNavItem.Discover,
                        BottomNavItem.Saved,
                        BottomNavItem.Profile
                    ).forEach { screen -&gt;
                        NavigationBarItem(
                            selected = currentRoute.value == screen.route,
                            onClick = {
                                currentRoute.value = screen.route
                                navController.navigate(screen)
                            },
                            icon = {
                                Icon(
                                    painter = painterResource(screen.icon),
                                    contentDescription = stringResource(screen.route)
                                )
                            },
                            label = { Text(stringResource(screen.route)) },
                            alwaysShowLabel = <span class="hljs-literal">true</span>
                        )
                    }
                }
            },
        ) { paddingValues -&gt;
            NavHost(
                navController = navController,
                startDestination = BottomNavItem.Feed,
                modifier = Modifier.padding(paddingValues).padding(AppPadding.large)
            ) {
                composable&lt;BottomNavItem.Feed&gt; { FeedScreen() }
                composable&lt;BottomNavItem.Discover&gt; { DiscoverScreen() }
                composable&lt;BottomNavItem.Saved&gt; { SavedScreen() }
                composable&lt;BottomNavItem.Profile&gt; { ProfileScreen() }
            }
        }
    }
}
</code></pre>
<p>And that should be it. Hope it works for you As well as it works for me.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1734437486901/34c1f9ca-34e1-4287-8874-ebd54460117f.png" alt="Screenshot of an Android and an iOS emulator showing a CMP app displaying the same UI with shared code" class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Find 20 testers to publish Your Android App on the Play Store]]></title><description><![CDATA[If you created your Play Console account after November 13th 2023 and it is a “Developer account for personal use”, you are required to proceed to closed testing of your app before it can be published on the Play Store.

you must run a closed test fo...]]></description><link>https://blog.alphaomardiallo.com/find-20-testers-to-publish-your-android-app-on-the-play-store</link><guid isPermaLink="true">https://blog.alphaomardiallo.com/find-20-testers-to-publish-your-android-app-on-the-play-store</guid><category><![CDATA[PlayConsole]]></category><category><![CDATA[Google Play Console]]></category><category><![CDATA[Android]]></category><category><![CDATA[android app development]]></category><category><![CDATA[android apps]]></category><dc:creator><![CDATA[Alpha Diallo]]></dc:creator><pubDate>Mon, 09 Dec 2024 09:08:50 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/w33-zg-dNL4/upload/e058136f03942bf417b9627908e3ff4b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you created your Play Console account after November 13th 2023 and it is a “Developer account for personal use”, you are required to proceed to closed testing of your app before it can be published on the Play Store.</p>
<blockquote>
<p>you must run a closed test for your app with a minimum of 20 testers who have been opted-in for at least the last 14 days continuously. When you meet these criteria, you can apply for production access</p>
</blockquote>
<p>In this article, I will share with you my experience to make this step as painless as possible.</p>
<h2 id="heading-a-little-bit-of-context">A little bit of context</h2>
<p>Since November 13th, 2023, Google has tightened its rules for publishing applications on the Play Store. These changes aim to improve app quality for users, which I support. However, requiring 20 testers over a 14-day period seems a bit excessive to me. This policy specifically applies to ‘Developer accounts for personal use.’ You can find more information about it <a target="_blank" href="https://support.google.com/googleplay/android-developer/answer/14151465">here</a>. <strong>Now that we’ve set the context, how can we get 20 testers quickly?</strong></p>
<h2 id="heading-setup-your-test">Setup your test</h2>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733734516423/d19abe87-399f-4cac-8638-bf0f7980391e.png" alt class="image--center mx-auto" /></p>
<p>First, you need to set up your closed testing channel. Start by selecting ‘<strong>Closed Testing</strong>’ from the left-hand menu. Next, click on the ‘<strong>Create New Release</strong>’ button in the top-right corner to set up your test channel. Follow the on-screen instructions to complete the process.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733734592416/9683a971-8f02-4cca-b426-a0d7ac26dc8d.png" alt class="image--center mx-auto" /></p>
<p>Next, you’ll need to add testers to your channel. At this stage, you’ll face an important choice depending on the method you prefer: you can either add individual testers by providing their email addresses or use a Google Group to manage testers collectively.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1733734654808/0cfd340a-a45e-4a2f-8f02-e8e6d792526e.png" alt class="image--center mx-auto" /></p>
<h3 id="heading-use-specific-emails">Use specific emails</h3>
<p>If you already have your 20 testers, this is the easiest method. You can gather the emails of your testers and proceed quickly. However, the challenge for most is finding these 20 testers upfront, as they are required to keep the app for at least 14 days.</p>
<h3 id="heading-use-google-groups">Use google groups</h3>
<p>If you don’t already know your testers, you can create a <a target="_blank" href="https://medium.com/r/?url=https%3A%2F%2Fwww.google.com%2Furl%3Fsa%3Dt%26source%3Dweb%26rct%3Dj%26opi%3D89978449%26url%3Dhttps%3A%2F%2Fgroups.google.com%2Fmy-groups%26ved%3D2ahUKEwjI9Pek6eGJAxVSVKQEHSgpNTQQFnoECAoQAQ%26usg%3DAOvVaw26oD7bluE4tbCa7I5FAE4a">Google group</a>. Setup the group and ensure it’s open for anyone to join. It’s also a good idea to start a discussion within the group where you can share the necessary links (found at the bottom of the closed testing channel page) for users to access your app. This method works best when you don’t have email addresses for all your testers.</p>
<h2 id="heading-find-your-testers">Find your testers</h2>
<p>Now, let’s address the challenging (painful) part.</p>
<h3 id="heading-friends-family-and-other-social-circles">Friends, family and other social circles</h3>
<p>One method is to reach out to your social circles (In Real Life &amp; Online). If you’re fortunate, you may quickly find 20 people willing to keep your app installed and open it daily for 14 days. However, the main challenge is that many people you know may use iPhones or may not take the testing seriously. While this is the most obvious method, it’s not my personal favorite. Never really worked for me. For the setup of the channel, it’s obvious.</p>
<h3 id="heading-reddit">Reddit</h3>
<p>When I was looking for testers on for my second app, I discovered the sub Reddit <a target="_blank" href="https://medium.com/r/?url=https%3A%2F%2Fwww.reddit.com%2Fr%2FAndroidClosedTesting%2F">r/AndroidClosedTesting</a> that is :</p>
<blockquote>
<p>A sub for Android developers to post Play Store links for testing. This sub aims to provide people with testers as they may not have 20 testers</p>
</blockquote>
<p>Here’s what you need to do:</p>
<ol>
<li><p>Join the sub Reddit.</p>
</li>
<li><p>Make a post with links to your Google Group and app.</p>
</li>
<li><p>Test as many apps as possible, and share a screenshot as proof under other users’ posts so they’ll return the favor.</p>
</li>
</ol>
<p>This method works wonders because everyone has the same goal, making it a win-win situation. While testing, you might even discover some great apps. <strong>It’s my go-to method</strong>: the community is fantastic, and it gets results. While you could ask users for their email addresses, setting up your channel with a Google Group is much easier.</p>
<h3 id="heading-dedicated-apps-and-freelancers">Dedicated apps and freelancers</h3>
<p>If you’re short on time and don’t mind spending $10 to $20, you can hire ‘professional’ testers to evaluate your app. Simply search for ‘closed testing’ on Google (or your preferred platform) or visit well-known websites in the gig economy. I’ve tried this method myself, and it works. For setting up your channel, follow the provider’s recommendations.</p>
<h2 id="heading-finally">Finally</h2>
<p>Getting your app tested and approved for the Play Store can feel like a hurdle, but it doesn’t have to be a painful process. Whether you turn to your social circles, explore communities like Reddit, or hire professional testers, there are multiple ways to find the 20 testers you need. It’s all about being strategic, utilizing available resources, and choosing the method that works best for you. With the new Play Store requirements, meeting the 20-tester mandate may seem daunting, but it’s an achievable goal.</p>
<p>Good luck with your testing, and I hope this article helps you navigate the process smoothly!</p>
]]></content:encoded></item><item><title><![CDATA[Problem-solving is the key skill if you want to become a programmer.]]></title><description><![CDATA[I am an aspiring Android developer. My path started in march 2020 during the first lockdown due to the pandemic in France. 
I started to (re)learn HTML5, CSS3 on freecodecamp. However, I knew I did not want to be a front-end developer.
I explored dif...]]></description><link>https://blog.alphaomardiallo.com/problem-solving-is-the-key-skill-if-you-want-to-become-a-programmer</link><guid isPermaLink="true">https://blog.alphaomardiallo.com/problem-solving-is-the-key-skill-if-you-want-to-become-a-programmer</guid><category><![CDATA[#beginners #learningtocode #100daysofcode]]></category><category><![CDATA[Beginner Developers]]></category><category><![CDATA[problem solving skills]]></category><category><![CDATA[programing]]></category><dc:creator><![CDATA[Alpha Diallo]]></dc:creator><pubDate>Mon, 06 Sep 2021 08:28:32 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1630915480576/GS5oezqVa.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I am an aspiring Android developer. My path started in march 2020 during the first lockdown due to the pandemic in France. </p>
<p>I started to (re)learn HTML5, CSS3 on <a target="_blank" href="www.freecodecamp.org">freecodecamp</a>. However, I knew I did not want to be a front-end developer.</p>
<p>I explored different career paths, and while doing it I kept learning front-end technologies. </p>
<p><strong>However, I made one major mistake. I did not learn how to solve problems.</strong></p>
<h3 id="how-i-realized">How I realized?</h3>
<div class="embed-wrapper"><div class="embed-loading"><div class="loadingRow"></div><div class="loadingRow"></div></div><a class="embed-card" href="https://twitter.com/devatTheCave/status/1431919378115833856?s=19">https://twitter.com/devatTheCave/status/1431919378115833856?s=19</a></div>
<p>Solving problems is a skill, we all use it every day without being aware of it most of the time. It can be influenced by many factors, like playing chess, football, or anything else that involves finding a solution. But if you want to be a programmer, you need to train it in order to be better. 
<strong>You need to be able to break a problem to solve it by giving correct instructions to a stupid machine, a computer.</strong> 
The language you know and how good you know the syntax of it does not matter if you reached the point where you can write a program. If you cannot resolve the problem, it is useless. </p>
<h3 id="how-to-fix-it">How to fix it?</h3>
<p>If I could go back in time, I would add an hour or two to learn this skill. However, it is never too late to learn. It is a slow process, it takes time and it probably is a never ending learning path.</p>
<p>Personally, I learn it on <a target="_blank" href="www.france-ioi.org">France IOI</a>, it is free and currently available in English, French and Spanish. It also allows me to learn Java as well. </p>
<p>However there are many other ressources you could use. 
It does not have to be boring, many are games. </p>
<p>Do not hesitate to let me know in the comment section if you know a good resource to learn it. </p>
]]></content:encoded></item></channel></rss>