Skip to content

Commit

Permalink
Merge pull request #13 from aanorbel/feat/flavor-support
Browse files Browse the repository at this point in the history
* feat: add support for flavors
  • Loading branch information
aanorbel authored Jul 30, 2024
2 parents 263b775 + 6db278a commit d09e605
Show file tree
Hide file tree
Showing 23 changed files with 483 additions and 100 deletions.
162 changes: 160 additions & 2 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,30 @@ plugins {
alias(libs.plugins.ktlint)
}

val organization: String? by project

val appConfig =
mapOf(
"dw" to
AppConfig(
appId = "org.dw.probe",
appName = "News Media Scan",
srcRoot = "src/dwMain/kotlin",
resRoot = "src/dwMain/resources",
),
"ooni" to
AppConfig(
appId = "org.ooni.probe",
appName = "OONI Probe",
srcRoot = "src/ooniMain/kotlin",
resRoot = "src/ooniMain/resources",
),
)

val config = appConfig[organization] ?: appConfig["ooni"]!!

println("The current build flavor is set to $organization with app id set to ${config.appId}.")

kotlin {
androidTarget {
@OptIn(ExperimentalKotlinGradlePluginApi::class)
Expand Down Expand Up @@ -54,8 +78,11 @@ kotlin {
implementation(libs.kotlin.serialization)
implementation(libs.bundles.ui)
implementation(libs.bundles.tooling)
}

getByName("commonMain") {
kotlin.srcDir(config.srcRoot)
}
}
all {
languageSettings.optIn("kotlinx.coroutines.ExperimentalCoroutinesApi")
}
Expand All @@ -71,16 +98,24 @@ kotlin {
}
}

compose.resources {
customDirectory(
sourceSetName = "commonMain",
directoryProvider = provider { layout.projectDirectory.dir(config.resRoot) },
)
}

android {
namespace = "org.ooni.probe"
compileSdk = libs.versions.android.compileSdk.get().toInt()

defaultConfig {
applicationId = "org.ooni.probe"
applicationId = config.appId
minSdk = libs.versions.android.minSdk.get().toInt()
targetSdk = libs.versions.android.targetSdk.get().toInt()
versionCode = 1
versionName = "1.0"
resValue("string", "app_name", config.appName)
}
packaging {
resources {
Expand Down Expand Up @@ -121,3 +156,126 @@ ktlint {
}
additionalEditorconfig.put("ktlint_function_naming_ignore_when_annotated_with", "Composable")
}

tasks.register("copyCommonResourcesToFlavor") {
doLast {
val projectDir = project.projectDir.absolutePath

val sourceFile = File(projectDir, "src/commonMain/composeResources")

val destinationFile = File(projectDir, config.resRoot)

copyRecursive(sourceFile, destinationFile)
}
}

tasks.register("cleanCopiedCommonResourcesToFlavor") {
doLast {
val projectDir = project.projectDir.absolutePath

val destinationFile = File(projectDir, config.resRoot)
destinationFile.listFiles()?.forEach { folder ->
folder.listFiles()?.forEach { file ->
if (file.name == ".gitignore") {
file.readText().lines().forEach { line ->
if (line.isNotEmpty()) {
println("Removing $line")
File(folder, line).deleteRecursively()
}
}.also {
file.delete()
}
}
}
}
}
}

/**
* Configure the prepareComposeResourcesTaskForCommonMain task to depend on the copyCommonResourcesToFlavor task.
* This will ensure that the common resources are copied to the correct location before the task is executed.
*
* NOTE: Current limitation is that multiple resources directories are not supported.
*/
tasks.named("preBuild").configure {
dependsOn("copyCommonResourcesToFlavor")
}

tasks.named("clean").configure {
dependsOn("copyCommonResourcesToFlavor")
}

tasks.named("clean").configure {
dependsOn("cleanCopiedCommonResourcesToFlavor")
}

data class AppConfig(
val appId: String,
val appName: String,
val srcRoot: String,
val resRoot: String,
)

/**
* Ignore the copied file if it is not already ignored.
*
* @param filePath The path to the file to ignore.
* @param lineToAdd The line to add to the file.
*/
fun ignoreCopiedFileIfNotIgnored(
filePath: String,
lineToAdd: String,
) {
val file = File(filePath)

if (!file.exists()) {
file.createNewFile()
}

val fileContents = file.readText()

if (!fileContents.contains(lineToAdd)) {
file.appendText("\n$lineToAdd")
}
}

/**
* Copy files from one directory to another.
*
* @param from The source directory.
* @param to The destination directory.
*/
fun copyRecursive(
from: File,
to: File,
) {
if (!from.exists()) {
println("Source directory does not exist: $from")
return
}
from.listFiles()?.forEach { file ->
if (file.name != ".DS_Store") {
if (file.isDirectory) {
val newDir = File(to, file.name)
newDir.mkdir()
copyRecursive(file, newDir)
} else {
val destinationFile = File(to, file.name)
if (destinationFile.exists()) {
println("Overwriting $destinationFile")
destinationFile.delete()
}
if (!destinationFile.parentFile.exists()) {
destinationFile.parentFile.mkdirs()
}
file.copyTo(destinationFile).also {
println("Ignoring ${it.name}")
ignoreCopiedFileIfNotIgnored(
to.absolutePath + "/.gitignore",
it.name,
)
}
}
}
}
}
3 changes: 1 addition & 2 deletions composeApp/src/androidMain/res/values/strings.xml
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
<resources>
<string name="app_name">OONI Probe</string>
</resources>
</resources>
3 changes: 3 additions & 0 deletions composeApp/src/commonMain/composeResources/values/common.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
<resources>
<string name="run_tests">Run Test</string>
</resources>
4 changes: 2 additions & 2 deletions composeApp/src/commonMain/kotlin/org/ooni/probe/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ import androidx.navigation.compose.rememberNavController
import co.touchlab.kermit.Logger
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.ooni.probe.di.Dependencies
import org.ooni.probe.ui.Theme
import org.ooni.probe.ui.AppTheme
import org.ooni.probe.ui.navigation.Navigation

@Composable
@Preview
fun App(dependencies: Dependencies) {
val navController = rememberNavController()

Theme {
AppTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.ooni.probe.shared

import androidx.compose.material3.ColorScheme

interface ThemeConfigBase {
fun colorScheme(isDarkTheme: Boolean): ColorScheme
}
60 changes: 2 additions & 58 deletions composeApp/src/commonMain/kotlin/org/ooni/probe/ui/Theme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,71 +4,15 @@ import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Shapes
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

private val primaryColor = Color(0xff0588cb)
private val primaryLightColor = primaryColor.copy(alpha = 0.75f)
private val secondaryColor = Color(0xff5db8fe)
private val secondaryLightColor = secondaryColor.copy(alpha = 0.75f)
private val primaryTextColor = Color(0xffffffff)
private val secondaryTextColor = Color(0xff000000)
private val surfaceDark = Color(0xFF161616)
private val surfaceLight = Color(0xFFFFFFFF)
private val backgroundLightColor = Color(0xffF1F0F5)
private val backgroundDarkColor = Color(0xff010100)
private val errorColor = Color(0xFFFF8989)
private val onErrorColor = Color(0xFF000000)

private val LightColors =
lightColorScheme(
primary = primaryColor,
onPrimary = primaryTextColor,
secondary = secondaryColor,
onSecondary = secondaryTextColor,
tertiary = primaryLightColor,
onTertiary = primaryTextColor,
background = backgroundLightColor,
onBackground = Color.Black,
surface = surfaceLight,
onSurface = Color.Black,
surfaceVariant = surfaceLight,
onSurfaceVariant = Color.Black,
secondaryContainer = primaryColor,
onSecondaryContainer = Color.White,
error = errorColor,
onError = onErrorColor,
)

private val DarkColors =
darkColorScheme(
primary = primaryColor,
onPrimary = primaryTextColor,
secondary = secondaryLightColor,
onSecondary = secondaryTextColor,
tertiary = primaryLightColor,
onTertiary = primaryTextColor,
background = backgroundDarkColor,
onBackground = Color.White,
surface = surfaceDark,
onSurface = Color.White,
surfaceVariant = surfaceDark,
onSurfaceVariant = Color.White,
secondaryContainer = primaryColor,
onSecondaryContainer = Color.White,
error = errorColor,
onError = onErrorColor,
)

@Composable
internal fun Theme(
fun AppTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
val colorScheme = if (useDarkTheme) DarkColors else LightColors
val colorScheme = ThemeConfig.colorScheme(useDarkTheme)
val shapes =
Shapes(
small = RoundedCornerShape(4.dp),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,73 @@
package org.ooni.probe.ui.dashboard

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import ooniprobe.composeapp.generated.resources.Res
import ooniprobe.composeapp.generated.resources.app_name
import ooniprobe.composeapp.generated.resources.logo
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview
import org.ooni.probe.ui.Theme
import org.ooni.probe.ui.AppTheme

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun DashboardScreen(
state: DashboardViewModel.State,
onEvent: (DashboardViewModel.Event) -> Unit,
) {
Column {
Button(
onClick = { onEvent(DashboardViewModel.Event.StartClick) },
enabled = !state.isRunning,
Scaffold(
topBar = {
TopAppBar(
title = {
Text(stringResource(Res.string.app_name))
},
)
},
) { contentPadding ->

Column(
modifier = Modifier.padding(contentPadding),
verticalArrangement = Arrangement.Center,
) {
Text("Run Test")
}
Button(
onClick = { onEvent(DashboardViewModel.Event.StartClick) },
enabled = !state.isRunning,
) {
// Text(stringResource(Res.string.run_tests))
Text("Run Tests")
}

Text(
text = state.log,
modifier =
Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState()),
)
Image(
painterResource(Res.drawable.logo),
contentDescription = "OONI Probe Logo",
modifier = Modifier.align(Alignment.CenterHorizontally),
)
Text(
text = state.log,
modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState()),
)
}
}
}

@Preview
@Composable
fun DashboardScreenPreview() {
Theme {
AppTheme {
DashboardScreen(
state = DashboardViewModel.State(isRunning = false, log = ""),
onEvent = {},
Expand Down
Loading

0 comments on commit d09e605

Please sign in to comment.