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
12 changes: 12 additions & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import org.jetbrains.kotlin.util.prefixIfNot

plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
Expand All @@ -17,13 +19,22 @@ android {
namespace = "com.paulcoding.hviewer"
compileSdk = 35

val repoUrl = providers.exec {
commandLine = "git remote get-url origin".split(' ')
}.standardOutput.asText.get().trim().removePrefix("https://github.com/")
.removePrefix("git@github.com:")
.removeSuffix(".git")
.prefixIfNot("https://github.com/")

defaultConfig {
applicationId = "com.paulcoding.hviewer"
minSdk = 26
targetSdk = 34
versionCode = 1
versionName = "1.3.2"

buildConfigField("String", "REPO_URL", "\"$repoUrl\"")

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

splits {
Expand Down Expand Up @@ -101,6 +112,7 @@ dependencies {
implementation(libs.ktor.serialization.gson)
implementation(libs.ktor.client.content.negotiation)
implementation(libs.ktor.client.logging)
implementation(libs.androidx.webkit)

implementation(libs.androidx.room.runtime)
ksp(libs.androidx.room.compiler)
Expand Down
10 changes: 10 additions & 0 deletions app/src/main/java/com/paulcoding/hviewer/extensions/Activity.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.paulcoding.hviewer.extensions

import android.app.Activity
import android.content.Intent
import android.net.Uri

fun Activity.openInBrowser(url: String) {
val intent = Intent(Intent.ACTION_VIEW, Uri.parse(url))
startActivity(intent)
}
9 changes: 9 additions & 0 deletions app/src/main/java/com/paulcoding/hviewer/ui/page/AppEntry.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.paulcoding.hviewer.ui.page.posts.PostsPage
import com.paulcoding.hviewer.ui.page.search.SearchPage
import com.paulcoding.hviewer.ui.page.settings.SettingsPage
import com.paulcoding.hviewer.ui.page.sites.SitesPage
import com.paulcoding.hviewer.ui.page.web.WebPage

@Composable
fun AppEntry() {
Expand Down Expand Up @@ -116,6 +117,10 @@ fun AppEntry() {
animatedComposable(Route.POST) {
PostPage(
appViewModel,
navToWebView = {
appViewModel.setWebViewUrl(it)
navController.navigate(Route.WEBVIEW)
},
goBack = {
navController.popBackStack()
})
Expand Down Expand Up @@ -189,6 +194,10 @@ fun AppEntry() {
deleteHistory = appViewModel::deleteHistory
)
}
animatedComposable(Route.WEBVIEW) {
val url = appViewModel.getWebViewUrl()
WebPage(goBack = { navController.popBackStack() }, url = url)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,18 @@ class AppViewModel : ViewModel() {

data class UiState(
val post: PostItem = PostItem(),
val url: String = "",
val site: Pair<String, SiteConfig> = "" to SiteConfig(),
val tag: Tag = Tag(),
val isDevMode: Boolean = BuildConfig.DEBUG,
)

fun setWebViewUrl(url: String) {
_stateFlow.update { it.copy(url = url) }
}

fun getWebViewUrl() = _stateFlow.value.url

fun setDevMode(isDevMode: Boolean) {
_stateFlow.update { it.copy(isDevMode = isDevMode) }
}
Expand Down
1 change: 1 addition & 0 deletions app/src/main/java/com/paulcoding/hviewer/ui/page/Route.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@ object Route {
const val LIST_SCRIPT = "list_script"
const val LOCK = "lock"
const val HISTORY = "history"
const val WEBVIEW = "webview"
}
60 changes: 51 additions & 9 deletions app/src/main/java/com/paulcoding/hviewer/ui/page/post/PostPage.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.paulcoding.hviewer.ui.page.post

import android.annotation.SuppressLint
import android.widget.Toast
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.clickable
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
Expand All @@ -14,6 +15,8 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
Expand All @@ -25,13 +28,17 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.viewmodel.compose.viewModel
import com.paulcoding.hviewer.MainActivity
import com.paulcoding.hviewer.MainApp.Companion.appContext
import com.paulcoding.hviewer.extensions.isScrolledToEnd
import com.paulcoding.hviewer.extensions.isScrollingUp
import com.paulcoding.hviewer.extensions.openInBrowser
import com.paulcoding.hviewer.helper.makeToast
import com.paulcoding.hviewer.ui.component.HBackIcon
import com.paulcoding.hviewer.ui.component.HGoTop
Expand All @@ -46,8 +53,9 @@ import me.saket.telephoto.zoomable.ZoomSpec
import me.saket.telephoto.zoomable.rememberZoomableState
import me.saket.telephoto.zoomable.zoomable

@SuppressLint("UseOfNonLambdaOffsetOverload")
@Composable
fun PostPage(appViewModel: AppViewModel, goBack: () -> Unit) {
fun PostPage(appViewModel: AppViewModel, navToWebView: (String) -> Unit, goBack: () -> Unit) {
val appState by appViewModel.stateFlow.collectAsState()
val post = appState.post
val siteConfig = appState.site.second
Expand Down Expand Up @@ -84,17 +92,14 @@ fun PostPage(appViewModel: AppViewModel, goBack: () -> Unit) {
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(uiState.images, key = { it }) { image ->
HImage(
modifier = Modifier.clickable { selectedImage = image },
url = image
)
PostImage(url = image) {
selectedImage = image
}
}
if (uiState.isLoading)
item {
Box(
modifier = Modifier
.statusBarsPadding()
.statusBarsPadding()
modifier = Modifier.statusBarsPadding()
) {
HLoading()
}
Expand Down Expand Up @@ -164,4 +169,41 @@ fun ImageModal(url: String, dismiss: () -> Unit) {
}
}
}
}

@Composable
fun PostImage(url: String, onTap: () -> Unit = {}) {
val showMenu = remember { mutableStateOf(false) }
val menuOffset = remember { mutableStateOf(Pair(0f, 0f)) }
val context = LocalContext.current as MainActivity

Box(modifier = Modifier.pointerInput(Unit) {
detectTapGestures(
onLongPress = { offset ->
println("pressed $url")
showMenu.value = true
menuOffset.value = Pair(offset.x, offset.y)
},
onTap = { onTap() }
)
}) {
HImage(
url = url
)

DropdownMenu(
expanded = showMenu.value,
onDismissRequest = { showMenu.value = false },
) {
DropdownMenuItem(
onClick = {
showMenu.value = false
context.openInBrowser(url)
},
text = {
Text("Open in browser")
}
)
}
}
}
23 changes: 17 additions & 6 deletions app/src/main/java/com/paulcoding/hviewer/ui/page/posts/PostCard.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,12 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.paulcoding.hviewer.MainActivity
import com.paulcoding.hviewer.extensions.openInBrowser
import com.paulcoding.hviewer.model.PostItem
import com.paulcoding.hviewer.model.Tag
import com.paulcoding.hviewer.ui.component.HFavoriteIcon
Expand Down Expand Up @@ -69,6 +72,7 @@ fun PostCard(
) {
var isBottomSheetVisible by remember { mutableStateOf(false) }
val bottomSheetState = rememberModalBottomSheetState()
val context = LocalContext.current as MainActivity

LaunchedEffect(isBottomSheetVisible) {
if (isBottomSheetVisible) {
Expand Down Expand Up @@ -123,12 +127,19 @@ fun PostCard(
SelectionContainer {
Text(text = name, fontSize = 20.sp)
}
Text(
text = url,
textDecoration = TextDecoration.Underline,
fontSize = 12.sp,
color = Color.Blue
)
TextButton(
onClick = {
isBottomSheetVisible = false
context.openInBrowser(url)
},
) {
Text(
text = url,
textDecoration = TextDecoration.Underline,
fontSize = 12.sp,
color = Color.Blue
)
}
if (size != null) {
Text(text = "Size: $size")
}
Expand Down
29 changes: 29 additions & 0 deletions app/src/main/java/com/paulcoding/hviewer/ui/page/web/WebPage.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.paulcoding.hviewer.ui.page.web

import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import com.paulcoding.hviewer.ui.component.HBackIcon

@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun WebPage(goBack: () -> Unit, url: String) {
Scaffold(
modifier = Modifier.fillMaxSize(),
topBar = {
TopAppBar(
title = {},
navigationIcon = {
HBackIcon { goBack() }
},
)
}
) { paddings ->
HWebView(modifier = Modifier.padding(paddings), url = url)
}
}

40 changes: 40 additions & 0 deletions app/src/main/java/com/paulcoding/hviewer/ui/page/web/WebView.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.paulcoding.hviewer.ui.page.web

import android.annotation.SuppressLint
import android.webkit.JavascriptInterface
import android.webkit.WebView
import android.webkit.WebViewClient
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView

@SuppressLint("SetJavaScriptEnabled")
@Composable
fun HWebView(
modifier: Modifier = Modifier,
url: String,
) {
class WebAppInterface() {
@JavascriptInterface
fun senData(data: String) {
println(data)
}
}
AndroidView(
modifier = modifier,
factory = { context ->
WebView(context).apply {
settings.javaScriptEnabled = true
webViewClient = object : WebViewClient() {
override fun onPageFinished(webview: WebView, url: String?) {
super.onPageFinished(webview, url)
webview.loadUrl("javascript:window.HViewer.senData('Hello from WebView')")
}
}
val jsInterface = WebAppInterface()
addJavascriptInterface(jsInterface, "HViewer")
loadUrl(url)
}
}
)
}
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ mmkv = "2.0.0"
navigationCompose = "2.8.5"
rhino = "1.7.15"
roomRuntime = "2.6.1"
webkit = "1.13.0-alpha02"
zoomable = "0.14.0"
material3 = "1.3.1"

Expand All @@ -43,6 +44,7 @@ androidx-navigation-compose = { module = "androidx.navigation:navigation-compose
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "roomRuntime" }
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "roomRuntime" }
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "roomRuntime" }
androidx-webkit = { module = "androidx.webkit:webkit", version.ref = "webkit" }
bom = { module = "io.github.Rosemoe.sora-editor:bom", version.ref = "bom" }
coil-compose = { module = "io.coil-kt.coil3:coil-compose", version.ref = "coilNetworkOkhttp" }
coil-gif = { module = "io.coil-kt.coil3:coil-gif", version.ref = "coilNetworkOkhttp" }
Expand Down
Loading