This commit is contained in:
tom5079
2021-09-18 14:48:55 +09:00
parent c2626cdee4
commit 14c64299ec
8 changed files with 117 additions and 41 deletions

View File

@@ -1,17 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetDropDown">
<targetSelectedWithDropDown>
<runningDeviceTargetSelectedWithDropDown>
<Target>
<type value="QUICK_BOOT_TARGET" />
<type value="RUNNING_DEVICE_TARGET" />
<deviceKey>
<Key>
<type value="VIRTUAL_DEVICE_PATH" />
<value value="$USER_HOME$/.android/avd/Pixel_3a_API_30_x86.avd" />
<type value="SERIAL_NUMBER" />
<value value="ce021712e3b19b2b04" />
</Key>
</deviceKey>
</Target>
</targetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2021-09-15T00:09:20.844719Z" />
</runningDeviceTargetSelectedWithDropDown>
<timeTargetWasSelectedWithDropDown value="2021-09-17T13:27:59.107239Z" />
</component>
</project>

View File

@@ -74,7 +74,7 @@ dependencies {
implementation("androidx.compose.runtime:runtime-livedata:1.0.2")
implementation("androidx.compose.material:material-icons-extended:1.0.2")
implementation("androidx.activity:activity-compose:1.3.1")
implementation("androidx.navigation:navigation-compose:2.4.0-alpha08")
implementation("androidx.navigation:navigation-compose:2.4.0-alpha09")
implementation("com.google.accompanist:accompanist-flowlayout:0.16.1")
implementation("com.google.accompanist:accompanist-appcompat-theme:0.16.0")

View File

@@ -44,6 +44,8 @@ import io.ktor.client.features.json.*
import io.ktor.client.features.json.serializer.*
import org.kodein.di.*
import org.kodein.di.android.x.androidXModule
import org.kodein.log.LoggerFactory
import org.kodein.log.newLogger
import xyz.quaver.io.FileX
import xyz.quaver.pupil.db.databaseModule
import xyz.quaver.pupil.sources.sourceModule

View File

@@ -64,7 +64,7 @@ abstract class Source {
@Composable
open fun SearchResult(itemInfo: ItemInfo, onEvent: ((SearchResultEvent) -> Unit)? = null) { }
open fun getHeadersBuilderForImage(itemID: String, url: String): HeadersBuilder.() -> Unit = { }
open fun getHeadersForImage(itemID: String, url: String): Map<String, String> = emptyMap()
open fun onSuggestionBind(binding: SearchSuggestionItemBinding, item: SearchSuggestion) {
binding.leftIcon.setImageResource(R.drawable.tag)

View File

@@ -225,9 +225,9 @@ class Hitomi(app: Application) : Source(), DIAware {
FullSearchResult(itemInfo = itemInfo)
}
override fun getHeadersBuilderForImage(itemID: String, url: String): HeadersBuilder.() -> Unit = {
append("Referer", getReferer(itemID.toInt()))
}
override fun getHeadersForImage(itemID: String, url: String) = mapOf(
"Referer" to getReferer(itemID.toInt())
)
override fun onSuggestionBind(binding: SearchSuggestionItemBinding, item: SearchSuggestion) {
item as TagSuggestion

View File

@@ -23,10 +23,11 @@ import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Scaffold
import androidx.compose.material.Text
import androidx.compose.material.TopAppBar
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Fullscreen
import androidx.compose.runtime.getValue
@@ -34,13 +35,23 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.core.view.WindowCompat
import androidx.compose.ui.unit.dp
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import coil.annotation.ExperimentalCoilApi
import coil.compose.rememberImagePainter
import coil.request.ImageRequest
import coil.transform.BlurTransformation
import com.google.accompanist.appcompattheme.AppCompatTheme
import io.ktor.http.*
import okhttp3.Headers
import org.kodein.di.DIAware
import org.kodein.di.android.closestDI
import org.kodein.log.LoggerFactory
import org.kodein.log.newLogger
import xyz.quaver.pupil.R
import xyz.quaver.pupil.ui.composable.FloatingActionButtonState
import xyz.quaver.pupil.ui.composable.MultipleFloatingActionButton
@@ -52,12 +63,24 @@ class ReaderActivity : ComponentActivity(), DIAware {
private val model: ReaderViewModel by viewModels()
private val logger = newLogger(LoggerFactory.default)
@OptIn(ExperimentalCoilApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model.handleIntent(intent)
model.load()
setContent {
var isFABExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
val isFullscreen by model.isFullscreen.observeAsState(false)
val title by model.title.observeAsState(stringResource(R.string.reader_loading))
val sourceIcon by model.sourceIcon.observeAsState()
val images by model.images.observeAsState(emptyList())
val source by model.sourceInstance.observeAsState()
logger.debug { "target: ${R.drawable.hitomi} value: $sourceIcon" }
WindowInsetsControllerCompat(window, window.decorView).run {
if (isFullscreen) {
@@ -74,30 +97,69 @@ class ReaderActivity : ComponentActivity(), DIAware {
TopAppBar(
title = {
Text(
"Reader",
title,
color = MaterialTheme.colors.onSecondary
)
},
actions = {
sourceIcon?.let { sourceIcon ->
Image(
modifier = Modifier.size(36.dp),
painter = painterResource(id = sourceIcon),
contentDescription = null
)
}
}
)
},
floatingActionButton = {
MultipleFloatingActionButton(
items = listOf(
SubFabItem(
icon = Icons.Default.Fullscreen,
label = stringResource(id = R.string.reader_fab_fullscreen)
) {
model.isFullscreen.postValue(!isFullscreen)
if (!isFullscreen)
MultipleFloatingActionButton(
items = listOf(
SubFabItem(
icon = Icons.Default.Fullscreen,
label = stringResource(id = R.string.reader_fab_fullscreen)
) {
model.isFullscreen.postValue(!isFullscreen)
}
),
targetState = isFABExpanded,
onStateChanged = {
isFABExpanded = it
}
),
targetState = isFABExpanded,
onStateChanged = {
isFABExpanded = it
}
)
)
}
) {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(32.dp)
) {
items(images) { image ->
Image(
modifier = Modifier.fillMaxWidth().heightIn(128.dp, 1000.dp),
painter = rememberImagePainter(
ImageRequest.Builder(this@ReaderActivity)
.data(image)
.headers(
Headers.headersOf(
*(source!!.getHeadersForImage(model.itemID.value!!, image).entries.fold(arrayOf()) { acc, entry ->
acc + entry.key + entry.value
})
).also {
logger.debug {
image
}
logger.debug {
it.toString()
}
}
)
.transformations(BlurTransformation(this@ReaderActivity, 1f))
.build()
),
contentDescription = null
)
}
}
}
}
}
@@ -107,4 +169,11 @@ class ReaderActivity : ComponentActivity(), DIAware {
super.onNewIntent(intent)
model.handleIntent(intent)
}
override fun onBackPressed() {
when {
model.isFullscreen.value == true -> model.isFullscreen.postValue(false)
else -> super.onBackPressed()
}
}
}

View File

@@ -74,7 +74,7 @@ fun MiniFloatingActionButton(
FloatingActionButton(
modifier = Modifier
.size(32.dp)
.size(40.dp)
.scale(buttonScale),
onClick = { onClick?.invoke(item) },
elevation = elevation,
@@ -185,7 +185,7 @@ fun MultipleFloatingActionButton(
}
MiniFloatingActionButton(
modifier = Modifier.padding(end = 12.dp),
modifier = Modifier.padding(end = 8.dp),
item = item,
buttonScale = buttonScale,
labelAlpha = labelAlpha,

View File

@@ -28,7 +28,10 @@ import io.ktor.client.request.*
import kotlinx.coroutines.*
import org.kodein.di.DIAware
import org.kodein.di.android.x.closestDI
import org.kodein.di.direct
import org.kodein.di.instance
import org.kodein.log.LoggerFactory
import org.kodein.log.newLogger
import xyz.quaver.pupil.adapters.ReaderItem
import xyz.quaver.pupil.db.AppDatabase
import xyz.quaver.pupil.db.Bookmark
@@ -42,6 +45,8 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
override val di by closestDI()
private val logger = newLogger(LoggerFactory.default)
val isFullscreen = MutableLiveData(false)
private val database: AppDatabase by instance()
@@ -72,11 +77,11 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
}
val sourceInstance = Transformations.map(source) {
source(it)
direct.source(it)
}
val sourceIcon = Transformations.map(sourceInstance) {
it.value.iconResID
it.iconResID
}
/**
@@ -89,18 +94,18 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
val uri = intent.data
val lastPathSegment = uri?.lastPathSegment
if (uri != null && lastPathSegment != null) {
_source.postValue(uri.host ?: error("Source cannot be null"))
_itemID.postValue(when (uri.host) {
_source.value = uri.host ?: error("Source cannot be null")
_itemID.value = when (uri.host) {
"hitomi.la" ->
Regex("([0-9]+).html").find(lastPathSegment)?.groupValues?.get(1) ?: error("Invalid itemID")
"hiyobi.me" -> lastPathSegment
"e-hentai.org" -> uri.pathSegments[1]
else -> error("Invalid host")
})
}
}
} else {
_source.postValue(intent.getStringExtra("source") ?: error("Invalid source"))
_itemID.postValue(intent.getStringExtra("id") ?: error("Invalid itemID"))
_source.value = intent.getStringExtra("source") ?: error("Invalid source")
_itemID.value = intent.getStringExtra("id") ?: error("Invalid itemID")
}
}
@@ -124,7 +129,7 @@ class ReaderViewModel(app: Application) : AndroidViewModel(app), DIAware {
viewModelScope.launch {
_images.postValue(withContext(Dispatchers.IO) {
source.images(itemID)
})
}!!)
}
}