WIP
This commit is contained in:
12
.idea/deploymentTargetDropDown.xml
generated
12
.idea/deploymentTargetDropDown.xml
generated
@@ -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>
|
||||||
@@ -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")
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -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,
|
||||||
|
|||||||
@@ -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)
|
||||||
})
|
}!!)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user