Skip to content

Commit

Permalink
added image scan
Browse files Browse the repository at this point in the history
  • Loading branch information
DatL4g committed Apr 16, 2024
1 parent 0c0757a commit b3466ff
Show file tree
Hide file tree
Showing 19 changed files with 383 additions and 52 deletions.
16 changes: 16 additions & 0 deletions .fleet/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"plugins": [
{
"type": "add",
"pluginName": "fleet.gradle"
},
{
"type": "add",
"pluginName": "fleet.kotlin"
},
{
"type": "add",
"pluginName": "fleet.mercury"
}
]
}
1 change: 1 addition & 0 deletions composeApp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ kotlin {
implementation(project(":anilist"))
implementation(project(":model"))
implementation(project(":settings"))
implementation(project(":trace"))
}

iosMain.dependencies {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package dev.datlag.aniflow.other

import android.net.Uri
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext

actual data class ImagePickerState(
private val mediaPicker: ManagedActivityResultLauncher<PickVisualMediaRequest, Uri?>
) {
actual fun launch() {
mediaPicker.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
}
}

@Composable
actual fun rememberImagePickerState(onPick: (ByteArray?) -> Unit): ImagePickerState {
val context = LocalContext.current
val mediaPicker = rememberLauncherForActivityResult(
contract = ActivityResultContracts.PickVisualMedia(),
onResult = { uri ->
onPick(
uri?.let {
context.contentResolver.openInputStream(it)?.use { input ->
input.readBytes()
}
}
)
}
)

return remember(mediaPicker) {
ImagePickerState(
mediaPicker
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import com.google.mlkit.nl.translate.Translation
import com.google.mlkit.nl.translate.TranslatorOptions
import dev.datlag.aniflow.SharedRes
import dev.icerock.moko.resources.compose.stringResource
import io.github.aakira.napier.Napier
import java.util.Locale

@Composable
Expand All @@ -24,15 +23,12 @@ actual fun TranslateButton(
) {
val locale = remember { Locale.getDefault() }
if (locale.language.equals(Locale.forLanguageTag("en").language, ignoreCase = true)) {
Napier.e("Language is english")
return
}
if (locale.toLanguageTag().equals("en", ignoreCase = true)) {
Napier.e("LanguageTag is english")
return
}
if (locale.isO3Language.equals("ENG", ignoreCase = true)) {
Napier.e("Language ISO is english")
return
}

Expand All @@ -43,7 +39,6 @@ actual fun TranslateButton(
}

if (targetLanguage == null || targetLanguage == TranslateLanguage.ENGLISH) {
Napier.e("TargetLanguage is: $targetLanguage, ${locale.toLanguageTag()}")
return
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import com.apollographql.apollo3.api.http.HttpRequest
import com.apollographql.apollo3.api.http.HttpResponse
import com.apollographql.apollo3.network.http.HttpInterceptor
import com.apollographql.apollo3.network.http.HttpInterceptorChain
import de.jensklingenberg.ktorfit.Ktorfit
import de.jensklingenberg.ktorfit.ktorfitBuilder
import dev.datlag.aniflow.BuildKonfig
import dev.datlag.aniflow.Sekret
import dev.datlag.aniflow.anilist.AiringTodayStateMachine
Expand All @@ -27,6 +29,8 @@ import org.kodein.di.bindSingleton
import org.kodein.di.instance
import dev.datlag.aniflow.common.nullableFirebaseInstance
import dev.datlag.aniflow.other.TokenRefreshHandler
import dev.datlag.aniflow.trace.Trace
import dev.datlag.aniflow.trace.TraceStateMachine
import dev.datlag.tooling.async.suspendCatching
import io.github.aakira.napier.Napier
import org.kodein.di.bindProvider
Expand Down Expand Up @@ -135,5 +139,22 @@ data object NetworkModule {
bindSingleton<TokenRefreshHandler> {
TokenRefreshHandler(instance())
}
bindSingleton<Ktorfit.Builder> {
ktorfitBuilder {
httpClient(instance<HttpClient>())
}
}
bindSingleton<Trace> {
val builder = instance<Ktorfit.Builder>()
builder.build {
baseUrl("https://api.trace.moe/")
}.create<Trace>()
}
bindProvider<TraceStateMachine> {
TraceStateMachine(
trace = instance(),
crashlytics = nullableFirebaseInstance()?.crashlytics
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package dev.datlag.aniflow.other

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.ImageBitmap
import io.ktor.utils.io.*

expect class ImagePickerState {
fun launch()
}

@Composable
expect fun rememberImagePickerState(onPick: (ByteArray?) -> Unit = { }): ImagePickerState
Original file line number Diff line number Diff line change
Expand Up @@ -61,21 +61,23 @@ fun CompactScreen(component: InitialComponent) {

when (val current = state) {
is FABConfig.Scan -> {
ExtendedFloatingActionButton(
onClick = current.onClick,
icon = {
Icon(
imageVector = Icons.Filled.CameraEnhance,
contentDescription = null
)
},
text = {
Text(
text = "Scan"
)
},
expanded = current.listState.isScrollingUp()
)
if (!current.loading) {
ExtendedFloatingActionButton(
onClick = current.onClick,
icon = {
Icon(
imageVector = Icons.Filled.CameraEnhance,
contentDescription = null
)
},
text = {
Text(
text = "Scan"
)
},
expanded = current.listState.isScrollingUp(),
)
}
}
else -> { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,21 +27,23 @@ fun ExpandedScreen(component: InitialComponent) {

when (val current = state) {
is FABConfig.Scan -> {
ExtendedFloatingActionButton(
onClick = current.onClick,
icon = {
Icon(
imageVector = Icons.Filled.CameraEnhance,
contentDescription = null
)
},
text = {
Text(
text = "Scan"
)
},
expanded = current.listState.isScrollingUp()
)
if (!current.loading) {
ExtendedFloatingActionButton(
onClick = current.onClick,
icon = {
Icon(
imageVector = Icons.Filled.CameraEnhance,
contentDescription = null
)
},
text = {
Text(
text = "Scan"
)
},
expanded = current.listState.isScrollingUp(),
)
}
}
else -> { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,23 @@ fun MediumScreen(component: InitialComponent) {

when (val current = state) {
is FABConfig.Scan -> {
ExtendedFloatingActionButton(
onClick = current.onClick,
icon = {
Icon(
imageVector = Icons.Filled.CameraEnhance,
contentDescription = null
)
},
text = {
Text(
text = "Scan"
)
},
expanded = current.listState.isScrollingUp()
)
if (!current.loading) {
ExtendedFloatingActionButton(
onClick = current.onClick,
icon = {
Icon(
imageVector = Icons.Filled.CameraEnhance,
contentDescription = null
)
},
text = {
Text(
text = "Scan"
)
},
expanded = current.listState.isScrollingUp(),
)
}
}
else -> { }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,19 @@ import dev.datlag.aniflow.anilist.PopularSeasonStateMachine
import dev.datlag.aniflow.anilist.TrendingAnimeStateMachine
import dev.datlag.aniflow.anilist.model.Medium
import dev.datlag.aniflow.anilist.state.SeasonState
import dev.datlag.aniflow.trace.TraceStateMachine
import dev.datlag.aniflow.ui.navigation.Component
import dev.datlag.aniflow.ui.navigation.ContentHolderComponent
import io.ktor.utils.io.*
import kotlinx.coroutines.flow.StateFlow

interface HomeComponent : ContentHolderComponent {
val airingState: StateFlow<AiringTodayStateMachine.State>
val trendingState: StateFlow<TrendingAnimeStateMachine.State>
val popularSeasonState: StateFlow<SeasonState>
val popularNextSeasonState: StateFlow<SeasonState>
val traceState: StateFlow<TraceStateMachine.State>

fun details(medium: Medium)
fun trace(channel: ByteArray)
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@ import dev.datlag.aniflow.LocalPaddingValues
import dev.datlag.aniflow.common.isScrollingUp
import dev.datlag.aniflow.common.plus
import dev.datlag.aniflow.other.StateSaver
import dev.datlag.aniflow.other.rememberImagePickerState
import dev.datlag.aniflow.ui.navigation.screen.initial.home.component.AiringOverview
import dev.datlag.aniflow.ui.navigation.screen.initial.home.component.PopularSeasonOverview
import dev.datlag.aniflow.ui.navigation.screen.initial.home.component.TrendingOverview
import dev.datlag.aniflow.ui.navigation.screen.initial.model.FABConfig
import dev.datlag.tooling.decompose.lifecycle.collectAsStateWithLifecycle

@Composable
fun HomeScreen(component: HomeComponent) {
Expand All @@ -44,11 +46,18 @@ private fun MainView(component: HomeComponent, modifier: Modifier = Modifier) {
initialFirstVisibleItemIndex = StateSaver.List.homeOverview,
initialFirstVisibleItemScrollOffset = StateSaver.List.homeOverviewOffset
)
val imagePicker = rememberImagePickerState {
it?.let(component::trace)
}
val traceState by component.traceState.collectAsStateWithLifecycle()

LaunchedEffect(listState) {
LaunchedEffect(listState, traceState) {
FABConfig.state.value = FABConfig.Scan(
listState = listState,
onClick = { }
loading = traceState.isLoading,
onClick = {
imagePicker.launch()
}
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import dev.datlag.aniflow.anilist.TrendingAnimeStateMachine
import dev.datlag.aniflow.anilist.model.Medium
import dev.datlag.aniflow.anilist.state.SeasonState
import dev.datlag.aniflow.common.onRender
import dev.datlag.aniflow.trace.TraceStateMachine
import dev.datlag.aniflow.ui.navigation.Component
import dev.datlag.aniflow.ui.navigation.screen.medium.MediumScreenComponent
import dev.datlag.tooling.compose.ioDispatcher
Expand Down Expand Up @@ -64,6 +65,15 @@ class HomeScreenComponent(
initialValue = PopularNextSeasonStateMachine.currentState
)

private val traceStateMachine by di.instance<TraceStateMachine>()
override val traceState: StateFlow<TraceStateMachine.State> = traceStateMachine.state.flowOn(
context = ioDispatcher()
).stateIn(
scope = ioScope(),
started = SharingStarted.WhileSubscribed(),
initialValue = TraceStateMachine.State.Waiting
)

@Composable
override fun render() {
onRender {
Expand All @@ -78,4 +88,10 @@ class HomeScreenComponent(
override fun details(medium: Medium) {
onMediumDetails(medium)
}

override fun trace(channel: ByteArray) {
launchIO {
traceStateMachine.dispatch(TraceStateMachine.Action.Load(channel))
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ sealed interface FABConfig {

data class Scan(
val listState: LazyListState,
val loading: Boolean = false,
val onClick: () -> Unit
) : FABConfig

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package dev.datlag.aniflow.other

import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.ImageBitmap
import io.ktor.utils.io.*

actual object ImagePickerState {
actual fun launch() {
}
}

@Composable
actual fun rememberImagePickerState(onPick: (ByteArray?) -> Unit): ImagePickerState {
return ImagePickerState
}
1 change: 1 addition & 0 deletions settings.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ include(":firebase")
include(":anilist")
include(":model")
include(":settings")
include(":trace")

pluginManagement {
repositories {
Expand Down
Loading

0 comments on commit b3466ff

Please sign in to comment.