Update to Material3

This also adds a setting to change the URL for the scanner
This commit is contained in:
William Brawner 2023-12-03 19:52:51 -07:00
parent c48b0e9440
commit 5941aaab38
Signed by: wbrawner
GPG key ID: 8FF12381C6C90D35
14 changed files with 393 additions and 121 deletions

View file

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="11" />
<bytecodeTargetLevel target="17" />
</component>
</project>

View file

@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<targetSelectedWithDropDown>
<Target>
<type value="QUICK_BOOT_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="C:\Users\billy\.android\avd\Pixel_4_API_30.avd" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2021-06-29T16:11:52.959392800Z" />
</component>
</project>

View file

@ -5,15 +5,15 @@
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings>
</option>
</component>

View file

@ -4,12 +4,13 @@ plugins {
}
android {
compileSdk = 30
namespace = "com.wbrawner.skerge"
compileSdk = 34
defaultConfig {
applicationId = "com.wbrawner.skerge"
minSdk = 26
targetSdk = 30
minSdk = 29
targetSdk = 34
versionCode = 1
versionName = "1.0"
@ -43,26 +44,30 @@ android {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = rootProject.extra["compose_version"] as String
kotlinCompilerExtensionVersion = "1.5.4"
}
}
dependencies {
implementation("androidx.core:core-ktx:1.5.0")
implementation("androidx.appcompat:appcompat:1.3.0")
implementation("com.google.android.material:material:1.3.0")
implementation("androidx.compose.ui:ui:${rootProject.extra["compose_version"]}")
implementation("androidx.compose.material:material:${rootProject.extra["compose_version"]}")
implementation("androidx.compose.ui:ui-tooling:${rootProject.extra["compose_version"]}")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.3.1")
implementation("androidx.activity:activity-compose:1.3.0-beta02")
val ktorVersion = "1.6.0"
val composeBom = platform("androidx.compose:compose-bom:2023.10.01")
implementation(composeBom)
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.10.0")
implementation("androidx.compose.ui:ui")
implementation("androidx.compose.material:material")
implementation("androidx.compose.material3:material3")
implementation("androidx.compose.ui:ui-tooling")
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
implementation("androidx.preference:preference-ktx:1.2.1")
implementation("androidx.activity:activity-compose:1.8.0")
val ktorVersion = "2.3.6"
implementation("io.ktor:ktor-client-cio:$ktorVersion")
implementation("io.ktor:ktor-client-android:$ktorVersion")
androidTestImplementation("io.ktor:ktor-client-mock:$ktorVersion")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.2")
androidTestImplementation("androidx.test.espresso:espresso-core:3.3.0")
androidTestImplementation("androidx.compose.ui:ui-test-junit4:${rootProject.extra["compose_version"]}")
androidTestImplementation(composeBom)
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
}

View file

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.wbrawner.skerge">
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.INTERNET" />
@ -16,6 +15,7 @@
android:name="com.wbrawner.skerge.MainActivity"
android:exported="true"
android:label="@string/app_name"
android:windowSoftInputMode="adjustPan|adjustResize"
android:theme="@style/Theme.Skerge.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

View file

@ -5,99 +5,198 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material.*
import androidx.compose.foundation.text.KeyboardActions
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material3.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.filled.Share
import androidx.compose.material.rememberSwipeableState
import androidx.compose.material.swipeable
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.input.KeyboardCapitalization
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.lifecycle.lifecycleScope
import androidx.core.view.WindowCompat
import com.wbrawner.skerge.ui.theme.SkergeTheme
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
class MainActivity : ComponentActivity() {
private val viewModel: MainViewModel by viewModels()
private val scansDirectory: File by lazy { File(cacheDir, "scans") }
private val viewModel: MainViewModel by viewModels {
MainViewModel.Factory(applicationContext)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
setContent {
val darkMode = isSystemInDarkTheme()
LaunchedEffect(darkMode) {
WindowCompat.getInsetsController(window, window.decorView)
.isAppearanceLightNavigationBars = !darkMode
}
val pages by viewModel.pages.collectAsState(initial = emptyList())
val scannerUrl by viewModel.scannerUrl.collectAsState(initial = "")
val coroutineScope = rememberCoroutineScope()
val context = LocalContext.current
val (loading, setLoading) = remember { mutableStateOf(false) }
var pdfShareJob: Job? by remember { mutableStateOf(null) }
SkergeTheme {
ScanScreen(
addButtonClicked = { viewModel.requestScan(scansDirectory) },
shareButtonClicked = { sharePdf(viewModel.pages.replayCache.first()) },
removePage = viewModel::removePage,
pagesFlow = viewModel.pages
)
loading = loading,
setLoading = { loading ->
if (!loading) {
pdfShareJob?.cancel()
}
}
}
private fun sharePdf(pages: List<Page>) {
// TODO: Show loading dialog for this
lifecycleScope.launch {
setLoading(loading)
},
scannerUrl = scannerUrl,
setScannerUrl = viewModel::setScannerUrl,
addButtonClicked = viewModel::requestScan,
shareButtonClicked = {
pdfShareJob = coroutineScope.launch {
delay(10_000)
val file = if (pages.size == 1) {
pages.first().file ?: return@launch
} else {
pages.merge()
}
startActivity(file.buildShareIntent(this@MainActivity))
startActivity(file.buildShareIntent(context))
}
},
removePage = viewModel::removePage,
pages = pages
)
}
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ScanScreen(
loading: Boolean,
setLoading: (Boolean) -> Unit,
scannerUrl: String,
setScannerUrl: (String) -> Unit,
addButtonClicked: () -> Unit,
shareButtonClicked: () -> Unit,
removePage: (Page) -> Unit,
pagesFlow: SharedFlow<List<Page>>
pages: List<Page>
) {
val pages = pagesFlow.collectAsState(initial = emptyList())
var showScannerUrlInput by remember { mutableStateOf(false) }
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(
modifier = Modifier.statusBarsPadding(),
title = { Text("Skerge") },
actions = {
IconButton(onClick = shareButtonClicked) {
Icon(imageVector = Icons.Default.Share, contentDescription = "Share")
}
IconButton(onClick = { showScannerUrlInput = true }) {
Icon(imageVector = Icons.Default.Settings, contentDescription = "Settings")
}
}
)
},
floatingActionButton = {
if (pages.value.none { it.file == null }) {
if (pages.none { it.file == null }) {
FloatingActionButton(onClick = addButtonClicked) {
Icon(imageVector = Icons.Default.Add, contentDescription = "Add")
}
}
}
) {
if (pages.value.isEmpty()) {
EmptyDocumentView()
) { paddingValues ->
if (pages.isEmpty()) {
EmptyDocumentView(modifier = Modifier.padding(paddingValues))
} else {
PageList(pages.value, removePage)
PageList(
modifier = Modifier.padding(paddingValues),
pages = pages,
removePage = removePage
)
}
if (loading) {
AlertDialog(
onDismissRequest = { setLoading(false) },
confirmButton = {
TextButton(onClick = { setLoading(false) }) {
Text("Cancel")
}
},
text = {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("Merging PDF...")
CircularProgressIndicator()
}
}
)
} else if (showScannerUrlInput) {
val (scannerInput, setScannerInput) = remember { mutableStateOf(scannerUrl) }
AlertDialog(
onDismissRequest = { showScannerUrlInput = false },
dismissButton = {
TextButton(onClick = { showScannerUrlInput = false }) {
Text("Cancel")
}
},
confirmButton = {
TextButton(
onClick = {
setScannerUrl(scannerInput)
showScannerUrlInput = false
}
) {
Text("Save")
}
},
text = {
OutlinedTextField(
value = scannerInput,
onValueChange = setScannerInput,
label = {
Text("Scanner URL")
},
keyboardActions = KeyboardActions {
setScannerUrl(scannerInput)
showScannerUrlInput = false
},
keyboardOptions = KeyboardOptions.Default.copy(
autoCorrect = false,
capitalization = KeyboardCapitalization.None,
keyboardType = KeyboardType.Uri
)
)
}
)
}
}
}
@Composable
fun EmptyDocumentView() {
fun EmptyDocumentView(modifier: Modifier) {
Column(
modifier = Modifier
modifier = modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.Center,
@ -113,8 +212,8 @@ fun EmptyDocumentView() {
}
@Composable
fun PageList(pages: List<Page>, removePage: (Page) -> Unit) {
LazyColumn(modifier = Modifier.fillMaxSize()) {
fun PageList(pages: List<Page>, removePage: (Page) -> Unit, modifier: Modifier) {
LazyColumn(modifier = modifier.fillMaxSize()) {
itemsIndexed(items = pages) { index, page ->
val topPadding = if (index == 0) 16.dp else 8.dp
val bottomPadding = if (index == pages.size - 1) 16.dp else 8.dp
@ -130,6 +229,7 @@ fun PageList(pages: List<Page>, removePage: (Page) -> Unit) {
}
}
@OptIn(ExperimentalMaterialApi::class, ExperimentalFoundationApi::class)
@Composable
fun PagePreview(page: Page, removePage: (Page) -> Unit) {
val (pageBitmap, setPageBitmap) = remember { mutableStateOf<Bitmap?>(null) }
@ -137,11 +237,26 @@ fun PagePreview(page: Page, removePage: (Page) -> Unit) {
setPageBitmap(page.loadBitmap().first)
}
pageBitmap?.let {
var showMenu by remember { mutableStateOf(false) }
Box(
modifier = Modifier.fillMaxSize()
) {
Image(
modifier = Modifier.fillMaxSize(),
modifier = Modifier
.fillMaxSize()
.combinedClickable(
onClick = {},
onLongClick = {
showMenu = true
}
),
bitmap = it.asImageBitmap(),
contentDescription = null
)
DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
DropdownMenuItem(text = { Text("Remove") }, onClick = { removePage(page) })
}
}
}
?: page.error?.let {
Column(
@ -173,7 +288,7 @@ fun LoadingPage() {
@Composable
fun DefaultPreview() {
SkergeTheme {
ScanScreen({}, {}, {}, MutableSharedFlow())
ScanScreen(false, {}, "", {}, {}, {}, {}, emptyList())
}
}

View file

@ -1,8 +1,12 @@
package com.wbrawner.skerge
import android.content.Context
import androidx.preference.PreferenceManager
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.CreationExtras
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
@ -10,11 +14,21 @@ import java.io.File
import java.io.IOException
import java.util.*
class MainViewModel(private val scannerService: ScannerService = HpScannerService()) : ViewModel() {
class MainViewModel(
private val scannerService: ScannerService,
private val fileDirectory: File
) : ViewModel() {
private val _pages = MutableStateFlow<List<Page>>(emptyList())
val pages = _pages.asSharedFlow()
private val _scannerUrl = MutableStateFlow(scannerService.url)
val scannerUrl = _scannerUrl.asSharedFlow()
fun requestScan(fileDirectory: File) {
fun setScannerUrl(url: String) {
scannerService.url = url
_scannerUrl.value = url
}
fun requestScan() {
val page = Page()
_pages.value = _pages.value.toMutableList().apply {
add(page)
@ -104,6 +118,20 @@ class MainViewModel(private val scannerService: ScannerService = HpScannerServic
}
_pages.value = updatedPages
}
class Factory(private val context: Context) : ViewModelProvider.Factory {
@Suppress("UNCHECKED_CAST")
override fun <T : ViewModel> create(
modelClass: Class<T>,
extras: CreationExtras
): T {
val sharedPreferences =
PreferenceManager.getDefaultSharedPreferences(context.applicationContext)
val scannerService = HpScannerService(sharedPreferences = sharedPreferences)
val fileDirectory = File(context.applicationContext.cacheDir, "scans")
return MainViewModel(scannerService, fileDirectory) as T
}
}
}
data class Page(

View file

@ -1,17 +1,25 @@
package com.wbrawner.skerge
import android.content.SharedPreferences
import androidx.core.content.edit
import io.ktor.client.*
import io.ktor.client.call.*
import io.ktor.client.engine.cio.*
import io.ktor.client.features.*
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.plugins.onDownload
import io.ktor.client.plugins.timeout
import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.client.utils.*
import io.ktor.http.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
import java.io.IOException
import java.util.concurrent.TimeUnit
interface ScannerService {
var url: String
suspend fun requestScan(scanSettings: ScanSettings = ScanSettings()): String
suspend fun getScanStatus(scanId: String): ScannerStatus
suspend fun downloadFile(
@ -21,20 +29,30 @@ interface ScannerService {
)
}
// TODO: It would be cool to be able to autodiscover the printer(s) or manually enter the details
const val SCANNER_URL = "http://brawner.print"
private const val KEY_SCANNER_URL = "scannerUrl"
class HpScannerService(
private val client: HttpClient = HttpClient(CIO) {
install(HttpTimeout) {
requestTimeoutMillis = TimeUnit.MINUTES.toMillis(2)
}
buildHeaders {
append("Content-Type", "text/xml")
}
}
},
private val sharedPreferences: SharedPreferences
) : ScannerService {
override var url: String
get() = sharedPreferences.getString(KEY_SCANNER_URL, null).orEmpty()
set(value) = sharedPreferences.edit { putString(KEY_SCANNER_URL, value) }
override suspend fun requestScan(scanSettings: ScanSettings): String {
val url = URLBuilder(SCANNER_URL).path("eSCL", "ScanJobs").build()
val url = URLBuilder(url).run {
path("eSCL", "ScanJobs")
build()
}
val response: HttpResponse = client.post(url) {
body = scanSettings.toXml()
setBody(scanSettings.toXml())
}
val location = response.headers["Location"]
?: throw IOException("Scanner didn't return location")
@ -42,7 +60,13 @@ class HpScannerService(
}
override suspend fun getScanStatus(scanId: String): ScannerStatus = ScannerStatus.fromXml(
client.get(URLBuilder(SCANNER_URL).path("eSCL", "ScannerStatus").build())
client.get(
URLBuilder(url)
.run {
path("eSCL", "ScannerStatus")
build()
}
).body()
)
override suspend fun downloadFile(
@ -51,13 +75,16 @@ class HpScannerService(
onProgress: (downloaded: Long, size: Long) -> Unit
) {
val httpResponse: HttpResponse = client.get(
URLBuilder(SCANNER_URL).path("eSCL", "ScanJobs", uuid, "NextDocument").build()
URLBuilder(url).run {
path("eSCL", "ScanJobs", uuid, "NextDocument")
build()
}
) {
onDownload { bytesSentTotal, contentLength ->
onProgress(bytesSentTotal, contentLength)
}
}
val responseBody: ByteArray = httpResponse.receive()
val responseBody: ByteArray = httpResponse.body()
destination.writeBytes(responseBody)
}
}

View file

@ -1,5 +1,67 @@
package com.wbrawner.skerge.ui.theme
import androidx.compose.ui.graphics.Color
val SkergeBlue = Color(0xFF0096d6)
val md_theme_light_primary = Color(0xFF006491)
val md_theme_light_onPrimary = Color(0xFFFFFFFF)
val md_theme_light_primaryContainer = Color(0xFFC9E6FF)
val md_theme_light_onPrimaryContainer = Color(0xFF001E2F)
val md_theme_light_secondary = Color(0xFF4F606E)
val md_theme_light_onSecondary = Color(0xFFFFFFFF)
val md_theme_light_secondaryContainer = Color(0xFFD3E5F5)
val md_theme_light_onSecondaryContainer = Color(0xFF0C1D29)
val md_theme_light_tertiary = Color(0xFF64597C)
val md_theme_light_onTertiary = Color(0xFFFFFFFF)
val md_theme_light_tertiaryContainer = Color(0xFFEADDFF)
val md_theme_light_onTertiaryContainer = Color(0xFF201635)
val md_theme_light_error = Color(0xFFBA1A1A)
val md_theme_light_errorContainer = Color(0xFFFFDAD6)
val md_theme_light_onError = Color(0xFFFFFFFF)
val md_theme_light_onErrorContainer = Color(0xFF410002)
val md_theme_light_background = Color(0xFFFCFCFF)
val md_theme_light_onBackground = Color(0xFF191C1E)
val md_theme_light_surface = Color(0xFFFCFCFF)
val md_theme_light_onSurface = Color(0xFF191C1E)
val md_theme_light_surfaceVariant = Color(0xFFDDE3EA)
val md_theme_light_onSurfaceVariant = Color(0xFF41474D)
val md_theme_light_outline = Color(0xFF71787E)
val md_theme_light_inverseOnSurface = Color(0xFFF0F0F3)
val md_theme_light_inverseSurface = Color(0xFF2E3133)
val md_theme_light_inversePrimary = Color(0xFF8ACEFF)
val md_theme_light_shadow = Color(0xFF000000)
val md_theme_light_surfaceTint = Color(0xFF006491)
val md_theme_light_outlineVariant = Color(0xFFC1C7CE)
val md_theme_light_scrim = Color(0xFF000000)
val md_theme_dark_primary = Color(0xFF8ACEFF)
val md_theme_dark_onPrimary = Color(0xFF00344D)
val md_theme_dark_primaryContainer = Color(0xFF004C6E)
val md_theme_dark_onPrimaryContainer = Color(0xFFC9E6FF)
val md_theme_dark_secondary = Color(0xFFB7C9D9)
val md_theme_dark_onSecondary = Color(0xFF22323F)
val md_theme_dark_secondaryContainer = Color(0xFF384956)
val md_theme_dark_onSecondaryContainer = Color(0xFFD3E5F5)
val md_theme_dark_tertiary = Color(0xFFCEC0E8)
val md_theme_dark_onTertiary = Color(0xFF352B4B)
val md_theme_dark_tertiaryContainer = Color(0xFF4C4163)
val md_theme_dark_onTertiaryContainer = Color(0xFFEADDFF)
val md_theme_dark_error = Color(0xFFFFB4AB)
val md_theme_dark_errorContainer = Color(0xFF93000A)
val md_theme_dark_onError = Color(0xFF690005)
val md_theme_dark_onErrorContainer = Color(0xFFFFDAD6)
val md_theme_dark_background = Color(0xFF191C1E)
val md_theme_dark_onBackground = Color(0xFFE2E2E5)
val md_theme_dark_surface = Color(0xFF191C1E)
val md_theme_dark_onSurface = Color(0xFFE2E2E5)
val md_theme_dark_surfaceVariant = Color(0xFF41474D)
val md_theme_dark_onSurfaceVariant = Color(0xFFC1C7CE)
val md_theme_dark_outline = Color(0xFF8B9198)
val md_theme_dark_inverseOnSurface = Color(0xFF191C1E)
val md_theme_dark_inverseSurface = Color(0xFFE2E2E5)
val md_theme_dark_inversePrimary = Color(0xFF006491)
val md_theme_dark_shadow = Color(0xFF000000)
val md_theme_dark_surfaceTint = Color(0xFF8ACEFF)
val md_theme_dark_outlineVariant = Color(0xFF41474D)
val md_theme_dark_scrim = Color(0xFF000000)
val seed = Color(0xFF0096D6)

View file

@ -1,35 +1,90 @@
package com.wbrawner.skerge.ui.theme
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material.MaterialTheme
import androidx.compose.material.darkColors
import androidx.compose.material.lightColors
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.runtime.Composable
private val DarkColorPalette = darkColors(
primary = SkergeBlue,
primaryVariant = SkergeBlue,
secondary = SkergeBlue
private val LightColors = lightColorScheme(
primary = md_theme_light_primary,
onPrimary = md_theme_light_onPrimary,
primaryContainer = md_theme_light_primaryContainer,
onPrimaryContainer = md_theme_light_onPrimaryContainer,
secondary = md_theme_light_secondary,
onSecondary = md_theme_light_onSecondary,
secondaryContainer = md_theme_light_secondaryContainer,
onSecondaryContainer = md_theme_light_onSecondaryContainer,
tertiary = md_theme_light_tertiary,
onTertiary = md_theme_light_onTertiary,
tertiaryContainer = md_theme_light_tertiaryContainer,
onTertiaryContainer = md_theme_light_onTertiaryContainer,
error = md_theme_light_error,
errorContainer = md_theme_light_errorContainer,
onError = md_theme_light_onError,
onErrorContainer = md_theme_light_onErrorContainer,
background = md_theme_light_background,
onBackground = md_theme_light_onBackground,
surface = md_theme_light_surface,
onSurface = md_theme_light_onSurface,
surfaceVariant = md_theme_light_surfaceVariant,
onSurfaceVariant = md_theme_light_onSurfaceVariant,
outline = md_theme_light_outline,
inverseOnSurface = md_theme_light_inverseOnSurface,
inverseSurface = md_theme_light_inverseSurface,
inversePrimary = md_theme_light_inversePrimary,
surfaceTint = md_theme_light_surfaceTint,
outlineVariant = md_theme_light_outlineVariant,
scrim = md_theme_light_scrim,
)
private val LightColorPalette = lightColors(
primary = SkergeBlue,
primaryVariant = SkergeBlue,
secondary = SkergeBlue
private val DarkColors = darkColorScheme(
primary = md_theme_dark_primary,
onPrimary = md_theme_dark_onPrimary,
primaryContainer = md_theme_dark_primaryContainer,
onPrimaryContainer = md_theme_dark_onPrimaryContainer,
secondary = md_theme_dark_secondary,
onSecondary = md_theme_dark_onSecondary,
secondaryContainer = md_theme_dark_secondaryContainer,
onSecondaryContainer = md_theme_dark_onSecondaryContainer,
tertiary = md_theme_dark_tertiary,
onTertiary = md_theme_dark_onTertiary,
tertiaryContainer = md_theme_dark_tertiaryContainer,
onTertiaryContainer = md_theme_dark_onTertiaryContainer,
error = md_theme_dark_error,
errorContainer = md_theme_dark_errorContainer,
onError = md_theme_dark_onError,
onErrorContainer = md_theme_dark_onErrorContainer,
background = md_theme_dark_background,
onBackground = md_theme_dark_onBackground,
surface = md_theme_dark_surface,
onSurface = md_theme_dark_onSurface,
surfaceVariant = md_theme_dark_surfaceVariant,
onSurfaceVariant = md_theme_dark_onSurfaceVariant,
outline = md_theme_dark_outline,
inverseOnSurface = md_theme_dark_inverseOnSurface,
inverseSurface = md_theme_dark_inverseSurface,
inversePrimary = md_theme_dark_inversePrimary,
surfaceTint = md_theme_dark_surfaceTint,
outlineVariant = md_theme_dark_outlineVariant,
scrim = md_theme_dark_scrim,
)
@Composable
fun SkergeTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable() () -> Unit) {
val colors = if (darkTheme) {
DarkColorPalette
fun SkergeTheme(
useDarkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable() () -> Unit
) {
val colors = if (!useDarkTheme) {
LightColors
} else {
LightColorPalette
DarkColors
}
MaterialTheme(
colors = colors,
typography = Typography,
shapes = Shapes,
colorScheme = colors,
content = content
)
}

View file

@ -5,13 +5,11 @@
<item name="colorPrimary">@color/skerge_blue</item>
<item name="colorPrimaryVariant">@color/skerge_blue</item>
<item name="colorOnPrimary">@color/white</item>
<!-- Secondary brand color. -->
<item name="colorSecondary">@color/skerge_blue</item>
<item name="colorSecondaryVariant">@color/skerge_blue</item>
<item name="colorOnSecondary">@color/black</item>
<!-- Status bar color. -->
<item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
<!-- Customize your theme here. -->
<item name="android:navigationBarColor">@android:color/transparent</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
<style name="Theme.Skerge.NoActionBar">

View file

@ -1,12 +1,11 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
val compose_version by extra("1.0.0-beta09")
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.0.0-beta05")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.10")
classpath("com.android.tools.build:gradle:8.1.2")
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.20")
}
}

View file

@ -1,6 +1,6 @@
#Tue Jun 29 09:32:45 MDT 2021
distributionBase=GRADLE_USER_HOME
distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-all.zip
distributionPath=wrapper/dists
zipStorePath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME

0
gradlew vendored Normal file → Executable file
View file