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

View File

@@ -74,7 +74,7 @@ dependencies {
implementation("androidx.compose.runtime:runtime-livedata:1.0.2") implementation("androidx.compose.runtime:runtime-livedata:1.0.2")
implementation("androidx.compose.material:material-icons-extended:1.0.2") implementation("androidx.compose.material:material-icons-extended:1.0.2")
implementation("androidx.activity:activity-compose:1.3.1") 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-flowlayout:0.16.1")
implementation("com.google.accompanist:accompanist-appcompat-theme:0.16.0") 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 io.ktor.client.features.json.serializer.*
import org.kodein.di.* import org.kodein.di.*
import org.kodein.di.android.x.androidXModule 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.io.FileX
import xyz.quaver.pupil.db.databaseModule import xyz.quaver.pupil.db.databaseModule
import xyz.quaver.pupil.sources.sourceModule import xyz.quaver.pupil.sources.sourceModule

View File

@@ -64,7 +64,7 @@ abstract class Source {
@Composable @Composable
open fun SearchResult(itemInfo: ItemInfo, onEvent: ((SearchResultEvent) -> Unit)? = null) { } 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) { open fun onSuggestionBind(binding: SearchSuggestionItemBinding, item: SearchSuggestion) {
binding.leftIcon.setImageResource(R.drawable.tag) binding.leftIcon.setImageResource(R.drawable.tag)

View File

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

View File

@@ -23,10 +23,11 @@ import android.os.Bundle
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.material.MaterialTheme import androidx.compose.foundation.Image
import androidx.compose.material.Scaffold import androidx.compose.foundation.layout.*
import androidx.compose.material.Text import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.TopAppBar import androidx.compose.foundation.lazy.items
import androidx.compose.material.*
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Fullscreen import androidx.compose.material.icons.filled.Fullscreen
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -34,13 +35,23 @@ import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource 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.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat 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 com.google.accompanist.appcompattheme.AppCompatTheme
import io.ktor.http.*
import okhttp3.Headers
import org.kodein.di.DIAware import org.kodein.di.DIAware
import org.kodein.di.android.closestDI 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.R
import xyz.quaver.pupil.ui.composable.FloatingActionButtonState import xyz.quaver.pupil.ui.composable.FloatingActionButtonState
import xyz.quaver.pupil.ui.composable.MultipleFloatingActionButton import xyz.quaver.pupil.ui.composable.MultipleFloatingActionButton
@@ -52,12 +63,24 @@ class ReaderActivity : ComponentActivity(), DIAware {
private val model: ReaderViewModel by viewModels() private val model: ReaderViewModel by viewModels()
private val logger = newLogger(LoggerFactory.default)
@OptIn(ExperimentalCoilApi::class)
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
model.handleIntent(intent)
model.load()
setContent { setContent {
var isFABExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) } var isFABExpanded by remember { mutableStateOf(FloatingActionButtonState.COLLAPSED) }
val isFullscreen by model.isFullscreen.observeAsState(false) 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 { WindowInsetsControllerCompat(window, window.decorView).run {
if (isFullscreen) { if (isFullscreen) {
@@ -74,13 +97,23 @@ class ReaderActivity : ComponentActivity(), DIAware {
TopAppBar( TopAppBar(
title = { title = {
Text( Text(
"Reader", title,
color = MaterialTheme.colors.onSecondary color = MaterialTheme.colors.onSecondary
) )
},
actions = {
sourceIcon?.let { sourceIcon ->
Image(
modifier = Modifier.size(36.dp),
painter = painterResource(id = sourceIcon),
contentDescription = null
)
}
} }
) )
}, },
floatingActionButton = { floatingActionButton = {
if (!isFullscreen)
MultipleFloatingActionButton( MultipleFloatingActionButton(
items = listOf( items = listOf(
SubFabItem( SubFabItem(
@@ -97,7 +130,36 @@ class ReaderActivity : ComponentActivity(), DIAware {
) )
} }
) { ) {
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) super.onNewIntent(intent)
model.handleIntent(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( FloatingActionButton(
modifier = Modifier modifier = Modifier
.size(32.dp) .size(40.dp)
.scale(buttonScale), .scale(buttonScale),
onClick = { onClick?.invoke(item) }, onClick = { onClick?.invoke(item) },
elevation = elevation, elevation = elevation,
@@ -185,7 +185,7 @@ fun MultipleFloatingActionButton(
} }
MiniFloatingActionButton( MiniFloatingActionButton(
modifier = Modifier.padding(end = 12.dp), modifier = Modifier.padding(end = 8.dp),
item = item, item = item,
buttonScale = buttonScale, buttonScale = buttonScale,
labelAlpha = labelAlpha, labelAlpha = labelAlpha,

View File

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