Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ package org.comixedproject.variant.android.view
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand All @@ -32,6 +33,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.comixedproject.variant.android.VariantTheme
import org.comixedproject.variant.android.view.comics.ComicBookView
import org.comixedproject.variant.android.view.reading.ReadingView
import org.comixedproject.variant.android.view.server.ServerView
import org.comixedproject.variant.android.view.settings.SettingsView
import org.comixedproject.variant.viewmodel.VariantViewModel
Expand All @@ -42,6 +44,7 @@ fun HomeView() {
val variantViewModel: VariantViewModel = koinViewModel()
var currentDestination by remember { mutableStateOf(AppDestination.COMICS) }
val coroutineScope = rememberCoroutineScope()
val comicBook by variantViewModel.comicBook.collectAsState()

Scaffold(
topBar = {
Expand All @@ -60,7 +63,22 @@ fun HomeView() {
},
content = { padding ->
when (currentDestination) {
AppDestination.COMICS -> ComicBookView(modifier = Modifier.padding(padding))
AppDestination.COMICS ->
if (comicBook != null) {
ReadingView(
comicBook!!,
modifier = Modifier.padding(padding),
onStopReading = { variantViewModel.readComicBook(null) }
)
} else {
ComicBookView(
onReadComicBook = { comicBook ->
variantViewModel.readComicBook(comicBook)
},
modifier = Modifier.padding(padding)
)
}

AppDestination.BROWSE -> ServerView(modifier = Modifier.padding(padding))
AppDestination.SETTINGS -> SettingsView(onCloseSettings = {
currentDestination = AppDestination.COMICS
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.comixedproject.variant.android.view.comics

import android.graphics.BitmapFactory
import androidx.compose.foundation.Image
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material3.CardDefaults
Expand Down Expand Up @@ -33,7 +34,11 @@ import org.comixedproject.variant.platform.Log
private val TAG = "ComicBookListItemView"

@Composable
fun ComicBookListItemView(comicBook: ComicBook, modifier: Modifier = Modifier) {
fun ComicBookListItemView(
comicBook: ComicBook,
onClick: (ComicBook) -> Unit,
modifier: Modifier = Modifier
) {
var coverContent by remember { mutableStateOf<ByteArray?>(null) }
val coroutineScope = rememberCoroutineScope()

Expand All @@ -44,7 +49,7 @@ fun ComicBookListItemView(comicBook: ComicBook, modifier: Modifier = Modifier) {
) {
val title = MetadataAPI.displayableTitle(comicBook)

Column {
Column(modifier = Modifier.clickable(onClick = { onClick(comicBook) })) {
comicBook.pages.firstOrNull()?.let { cover ->
if (coverContent == null) {
Image(
Expand Down Expand Up @@ -86,5 +91,5 @@ fun ComicBookListItemView(comicBook: ComicBook, modifier: Modifier = Modifier) {
@Composable
@Preview
fun ComicBookListItemViewPreview() {
VariantTheme { ComicBookListItemView(comicBook = COMIC_BOOK_LIST.get(0)) }
VariantTheme { ComicBookListItemView(comicBook = COMIC_BOOK_LIST.get(0), onClick = {}) }
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ import org.comixedproject.variant.platform.Log
private val TAG = "ComicBookListView"

@Composable
fun ComicBookListView(comicBookList: List<ComicBook>, modifier: Modifier = Modifier) {
fun ComicBookListView(
comicBookList: List<ComicBook>,
onClick: (ComicBook) -> Unit,
modifier: Modifier = Modifier
) {
Scaffold(
content = { padding ->
if (comicBookList.isEmpty()) {
Expand All @@ -55,7 +59,11 @@ fun ComicBookListView(comicBookList: List<ComicBook>, modifier: Modifier = Modif
horizontalArrangement = Arrangement.spacedBy(4.dp),
content = {
items(comicBookList) { comicBook ->
ComicBookListItemView(comicBook, modifier = Modifier.padding(padding))
ComicBookListItemView(
comicBook,
onClick = { onClick(it) },
modifier = Modifier.padding(padding)
)
}
})
}
Expand All @@ -67,6 +75,6 @@ fun ComicBookListView(comicBookList: List<ComicBook>, modifier: Modifier = Modif
@Preview
fun ComicBookListViewPreview() {
VariantTheme {
ComicBookListView(COMIC_BOOK_LIST)
ComicBookListView(COMIC_BOOK_LIST, onClick = {})
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,21 +24,22 @@ import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import org.comixedproject.variant.android.VariantTheme
import org.comixedproject.variant.model.library.ComicBook
import org.comixedproject.variant.viewmodel.VariantViewModel
import org.koin.androidx.compose.koinViewModel

private val TAG = "ComicBookView"

@Composable
fun ComicBookView(modifier: Modifier = Modifier) {
fun ComicBookView(onReadComicBook: (ComicBook) -> Unit, modifier: Modifier = Modifier) {
val variantViewModel: VariantViewModel = koinViewModel()
val comicBookList by variantViewModel.comicBookList.collectAsState()

ComicBookListView(comicBookList, modifier = modifier)
ComicBookListView(comicBookList, onClick = { onReadComicBook(it) }, modifier = modifier)
}

@Composable
@Preview
fun ComicBookViewPreview() {
VariantTheme { ComicBookView() }
VariantTheme { ComicBookView(onReadComicBook = { }) }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
/*
* Variant - A digital comic book reading application for the iPad and Android tablets.
* Copyright (C) 2025, The ComiXed Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>
*/

package org.comixedproject.variant.android.view.reading

import android.graphics.BitmapFactory
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import org.comixedproject.variant.adaptor.ArchiveAPI
import org.comixedproject.variant.android.COMIC_BOOK_LIST
import org.comixedproject.variant.android.R
import org.comixedproject.variant.android.VariantTheme
import org.comixedproject.variant.platform.Log

private const val TAG = "ReadingPageView"

@Composable
fun ReadingPageView(
comicFilename: String,
pageFilename: String,
title: String,
currentPage: Int,
totalPages: Int,
onChangePage: (Int) -> Unit,
onStopReading: () -> Unit,
modifier: Modifier = Modifier
) {
var currentPageContent by remember { mutableStateOf<ByteArray?>(null) }

Scaffold(
content = { padding ->
if (currentPageContent == null) {
LaunchedEffect(currentPageContent) {
currentPageContent =
ArchiveAPI.loadPage(comicFilename, pageFilename)
}
} else {
currentPageContent?.let { content ->
Image(
bitmap = BitmapFactory.decodeByteArray(content, 0, content.size)
.asImageBitmap(),
contentDescription = title,
modifier = Modifier
.padding(padding)
.fillMaxHeight()
)
}
}
},
topBar = {
Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
) {
IconButton(
onClick = {
Log.debug(
TAG,
"Closing comic book"
)
onStopReading()
}
) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.stopReadingLabel)
)
}

Text(
title,
style = MaterialTheme.typography.titleMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.fillMaxWidth()
)
}
},
bottomBar = {
BottomAppBar {
Row(modifier = Modifier.fillMaxWidth()) {
IconButton(onClick = {
currentPageContent = null
onChangePage(currentPage - 1)
}, enabled = (currentPage > 0)) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(R.string.previousPageLabel)
)
}

Slider(
value = currentPage.toFloat(),
valueRange = 0f..(totalPages - 1).toFloat(),
steps = totalPages,
onValueChange = {
currentPageContent = null
onChangePage(it.toInt())
},
modifier = Modifier.weight(0.9f)
)

IconButton(onClick = {
currentPageContent = null
onChangePage(currentPage + 1)
}, enabled = (currentPage < (totalPages - 1))) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = stringResource(R.string.nextPageLabel)
)
}
}
}
},
modifier = modifier
.fillMaxSize()
)
}


@Composable
@Preview
fun ReadingPageViewPreview() {
val comic = COMIC_BOOK_LIST.get(0)
VariantTheme {
ReadingPageView(
comic.filename,
comic.pages.get(0).filename,
"Page Title",
5,
10,
onChangePage = {},
onStopReading = {}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Variant - A digital comic book reading application for the iPad and Android tablets.
* Copyright (C) 2025, The ComiXed Project
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses>
*/

package org.comixedproject.variant.android.view.reading

import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import org.comixedproject.variant.android.COMIC_BOOK_LIST
import org.comixedproject.variant.android.VariantTheme
import org.comixedproject.variant.model.library.ComicBook
import org.comixedproject.variant.platform.Log

private const val TAG = "ReadingView"

@Composable
fun ReadingView(comicBook: ComicBook, onStopReading: () -> Unit, modifier: Modifier = Modifier) {
var currentPage by remember { mutableIntStateOf(0) }

ReadingPageView(
comicBook.path,
comicBook.pages.get(currentPage).filename,
comicBook.pages.get(currentPage).filename,
currentPage,
comicBook.pages.size,
onChangePage = {
Log.debug(TAG, "Going to page ${it}")
currentPage = it
},
onStopReading = onStopReading,
modifier = modifier
)
}

@Composable
@Preview
fun ReadingViewPreview() {
VariantTheme { ReadingView(COMIC_BOOK_LIST.get(0), onStopReading = {}) }
}
3 changes: 3 additions & 0 deletions androidVariant/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@
<string name="emptyComicListText">No comic books to show...</string>
<string name="comicFileSizeText">%1$s MB</string>
<string name="cancelButton">Cancel</string>
<string name="previousPageLabel">Go to the previous page.</string>
<string name="nextPageLabel">Go to the next page.</string>
<string name="stopReadingLabel">Stop reading comic book.</string>
</resources>
Loading