Update to Material3
This also adds a setting to change the URL for the scanner
This commit is contained in:
parent
c48b0e9440
commit
5941aaab38
14 changed files with 393 additions and 121 deletions
|
@ -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>
|
|
@ -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>
|
|
@ -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>
|
||||
|
|
|
@ -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")
|
||||
}
|
|
@ -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" />
|
||||
|
|
|
@ -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()) },
|
||||
loading = loading,
|
||||
setLoading = { loading ->
|
||||
if (!loading) {
|
||||
pdfShareJob?.cancel()
|
||||
}
|
||||
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(context))
|
||||
}
|
||||
},
|
||||
removePage = viewModel::removePage,
|
||||
pagesFlow = viewModel.pages
|
||||
pages = pages
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun sharePdf(pages: List<Page>) {
|
||||
// TODO: Show loading dialog for this
|
||||
lifecycleScope.launch {
|
||||
val file = if (pages.size == 1) {
|
||||
pages.first().file ?: return@launch
|
||||
} else {
|
||||
pages.merge()
|
||||
}
|
||||
startActivity(file.buildShareIntent(this@MainActivity))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@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 {
|
||||
Image(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
bitmap = it.asImageBitmap(),
|
||||
contentDescription = null
|
||||
)
|
||||
var showMenu by remember { mutableStateOf(false) }
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
Image(
|
||||
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())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
} else {
|
||||
LightColorPalette
|
||||
}
|
||||
fun SkergeTheme(
|
||||
useDarkTheme: Boolean = isSystemInDarkTheme(),
|
||||
content: @Composable() () -> Unit
|
||||
) {
|
||||
val colors = if (!useDarkTheme) {
|
||||
LightColors
|
||||
} else {
|
||||
DarkColors
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colors = colors,
|
||||
typography = Typography,
|
||||
shapes = Shapes,
|
||||
content = content
|
||||
)
|
||||
MaterialTheme(
|
||||
colorScheme = colors,
|
||||
content = content
|
||||
)
|
||||
}
|
|
@ -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">
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
}
|
||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -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
0
gradlew
vendored
Normal file → Executable file
Loading…
Reference in a new issue