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
10 changes: 5 additions & 5 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ on:
- release
- develop
tags:
- '**'
- "**"
paths-ignore:
- '**.md'
- '**.yml'
- "**.md"
- "**.yml"
pull_request:
branches:
- master
Expand All @@ -35,7 +35,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
distribution: 'jetbrains'
distribution: "jetbrains"
java-version: 25

- name: Setup Android SDK
Expand Down Expand Up @@ -87,7 +87,7 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
distribution: 'adopt'
distribution: "adopt"
java-version: 25

- name: Setup Android SDK
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import androidx.compose.ui.platform.ClipEntry
import androidx.compose.ui.platform.LocalClipboard
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.core.net.toUri
import dev.dimension.flare.R
import dev.dimension.flare.common.AndroidDownloadManager
import dev.dimension.flare.common.MediaFileNamePolicy
Expand Down Expand Up @@ -71,9 +70,9 @@ internal fun ProvideAndroidTimelineMediaActions(content: @Composable () -> Unit)
scope.launch {
clipboard.setClipEntry(
ClipEntry(
ClipData.newRawUri(
ClipData.newPlainText(
mediaLinkLabel,
media.url.toUri(),
media.url,
),
),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -665,7 +665,7 @@ private fun ArticleVideoBlock(
previewUri = media.thumbnailUrl,
contentDescription = media.description,
modifier = Modifier.fillMaxSize(),
muted = false,
muted = true,
showControls = true,
keepScreenOn = false,
aspectRatio = media.aspectRatio.coerceIn(0.2f, 4f),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.media3.common.C
import androidx.media3.common.util.UnstableApi
import androidx.media3.exoplayer.ExoPlayer
Expand All @@ -92,7 +91,6 @@ import coil3.imageLoader
import coil3.network.NetworkHeaders
import coil3.network.httpHeaders
import coil3.request.ImageRequest
import coil3.request.crossfade
import coil3.size.Size
import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
Expand Down Expand Up @@ -882,9 +880,9 @@ internal fun MediaViewerScreen(
val url = current.url
clipboard.setClipEntry(
ClipEntry(
ClipData.newRawUri(
ClipData.newPlainText(
label,
url.toUri(),
url,
),
),
)
Expand Down Expand Up @@ -1337,7 +1335,7 @@ private fun ImageItem(
.Builder(LocalContext.current)
.data(url)
.placeholderMemoryCacheKey(previewUrl)
.crossfade(1_000)
// .crossfade(1_000)
.size(Size.ORIGINAL)
.let { builder ->
if (customHeaders.isNullOrEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsTopHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.staggeredgrid.LazyStaggeredGridState
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
import androidx.compose.foundation.lazy.staggeredgrid.rememberLazyStaggeredGridState
Expand Down Expand Up @@ -64,7 +65,6 @@ import compose.icons.fontawesomeicons.Solid
import compose.icons.fontawesomeicons.solid.Robot
import dev.dimension.flare.R
import dev.dimension.flare.common.PagingState
import dev.dimension.flare.common.onSuccess
import dev.dimension.flare.common.refreshSuspend
import dev.dimension.flare.data.model.VideoAutoplay
import dev.dimension.flare.model.AccountType
Expand Down Expand Up @@ -101,9 +101,11 @@ import dev.dimension.flare.ui.model.onLoading
import dev.dimension.flare.ui.model.onSuccess
import dev.dimension.flare.ui.model.takeSuccessOr
import dev.dimension.flare.ui.presenter.home.TimelinePresenter
import dev.dimension.flare.ui.presenter.home.TimelineState
import dev.dimension.flare.ui.presenter.invoke
import dev.dimension.flare.ui.presenter.profile.ProfileMedia
import dev.dimension.flare.ui.presenter.profile.ProfileMediaPresenter
import dev.dimension.flare.ui.presenter.profile.ProfileMediaState
import dev.dimension.flare.ui.presenter.profile.ProfilePresenter
import dev.dimension.flare.ui.presenter.profile.ProfileState
import dev.dimension.flare.ui.presenter.profile.ProfileWithUserNameAndHostPresenter
Expand Down Expand Up @@ -493,21 +495,27 @@ internal fun ProfileScreen(
}
HorizontalPager(
state = pagerState,
key = { index ->
tabs.getOrNull(index)?.id ?: "profile_tab_$index"
},
) { index ->
val tab = tabs[index]
when (tab) {
is ProfileTabItem.Media -> {
val data = tab.presenter.body().mediaState
val tabState = rememberProfileMediaTabState(tab)
val data = tabState.mediaState
val latestData = rememberUpdatedState(data)
if (index == pagerState.currentPage) {
LaunchedEffect(tab, index) {
LaunchedEffect(tab.id, index) {
activeTabRefresh = {
latestData.value.refreshSuspend()
}
}
}
val gridState = rememberLazyStaggeredGridState()
ProfileMediaTab(
mediaState = data,
state = gridState,
onItemClicked = { statusKey, index, preview ->
onMediaClick(statusKey, index, preview)
},
Expand All @@ -516,10 +524,11 @@ internal fun ProfileScreen(
}

is ProfileTabItem.Timeline -> {
val data = tab.presenter.body().listState
val tabState = rememberProfileTimelineTabState(tab)
val data = tabState.listState
val latestData = rememberUpdatedState(data)
if (index == pagerState.currentPage) {
LaunchedEffect(tab, index) {
LaunchedEffect(tab.id, index) {
activeTabRefresh = {
latestData.value.refreshSuspend()
}
Expand Down Expand Up @@ -660,6 +669,7 @@ private fun ProfileInsightAction(
@Composable
private fun ProfileMediaTab(
mediaState: PagingState<ProfileMedia>,
state: LazyStaggeredGridState,
onItemClicked: (statusKey: MicroBlogKey, index: Int, preview: String?) -> Unit,
modifier: Modifier = Modifier,
) {
Expand All @@ -679,6 +689,7 @@ private fun ProfileMediaTab(
}
LazyVerticalStaggeredGrid(
modifier = modifier,
state = state,
columns = StaggeredGridCells.Adaptive(size),
verticalItemSpacing = 8.dp,
horizontalArrangement = Arrangement.spacedBy(8.dp),
Expand Down Expand Up @@ -737,6 +748,22 @@ private fun ProfileMediaTab(
}
}

@Composable
private fun rememberProfileTimelineTabState(tab: ProfileTabItem.Timeline): TimelineState {
val state by producePresenter(key = "profile_timeline_${tab.id}") {
remember(tab.id) { tab.presenter }.invoke()
}
return state
}

@Composable
private fun rememberProfileMediaTabState(tab: ProfileTabItem.Media): ProfileMediaState {
val state by producePresenter(key = "profile_media_${tab.id}") {
remember(tab.id) { tab.presenter }.invoke()
}
return state
}

@Composable
private fun profilePresenter(
userKey: MicroBlogKey?,
Expand All @@ -756,13 +783,15 @@ private fun profilePresenter(
when (it) {
is ProfileState.Tab.Timeline -> {
ProfileTabItem.Timeline(
id = it.id,
name = it.name,
presenter = it.presenter,
)
}

is ProfileState.Tab.Media -> {
ProfileTabItem.Media(
id = it.id,
name = it.name,
presenter = it.presenter,
)
Expand All @@ -784,14 +813,17 @@ private fun profilePresenter(
}

private sealed interface ProfileTabItem {
val id: String
val name: UiStrings

data class Timeline(
override val id: String,
override val name: UiStrings,
val presenter: TimelinePresenter,
) : ProfileTabItem

data class Media(
override val id: String,
override val name: UiStrings,
val presenter: ProfileMediaPresenter,
) : ProfileTabItem
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dev.dimension.flare.ui.screen.serviceselect

import android.net.Uri
import android.view.ViewGroup.LayoutParams
import android.webkit.CookieManager
import android.webkit.WebSettings
Expand All @@ -20,16 +19,13 @@ import dev.dimension.flare.ui.component.FlareScaffold
import kotlinx.coroutines.delay
import kotlin.time.Duration.Companion.seconds

private val USER_AGENT_HEADERS =
private val userAgent =
mapOf(
"User-Agent" to "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.3",
"user-agent" to "Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.3",
"Pragma" to "no-cache",
"Cache-Control" to "no-cache",
)

private const val ANDROID_USER_AGENT =
"Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Mobile Safari/537.3"

@Composable
internal fun WebCookieLoginScreen(
url: String,
Expand All @@ -38,16 +34,12 @@ internal fun WebCookieLoginScreen(
) {
val webViewState = rememberWebViewState(url)
LaunchedEffect(url) {
val cookieManager = CookieManager.getInstance()
while (true) {
webViewState.lastLoadedUrl?.let { loadedUrl ->
val cookies =
cookieManager.getCookieHeaderForUrls(
cookieLookupUrls(
loadedUrl = loadedUrl,
originalUrl = url,
),
)
CookieManager
.getInstance()
.getCookie(loadedUrl)
if (callback(cookies)) {
onBack()
break
Expand All @@ -70,51 +62,17 @@ internal fun WebCookieLoginScreen(
.background(MaterialTheme.colorScheme.background)
.padding(it)
.fillMaxSize(),
onCreated = { webView ->
onCreated = {
WebStorage.getInstance().deleteAllData()
val cookieManager = CookieManager.getInstance()
cookieManager.setAcceptCookie(true)
cookieManager.setAcceptThirdPartyCookies(webView, true)
with(webView.settings) {
userAgentString = ANDROID_USER_AGENT
CookieManager.getInstance().removeAllCookies(null)
with(it.settings) {
userAgentString = userAgent.toString()
javaScriptEnabled = true
domStorageEnabled = true
javaScriptCanOpenWindowsAutomatically = false
cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
}
cookieManager.removeAllCookies {
cookieManager.flush()
webView.loadUrl(url, USER_AGENT_HEADERS)
}
},
)
}
}

private fun cookieLookupUrls(
loadedUrl: String,
originalUrl: String,
): List<String> {
val loadedHost = loadedUrl.hostOrNull()
val originalHost = originalUrl.hostOrNull()
return if (loadedHost != null && originalHost != null && loadedHost.equals(originalHost, ignoreCase = true)) {
listOf(loadedUrl)
} else {
listOf(loadedUrl, originalUrl)
}
}

private fun CookieManager.getCookieHeaderForUrls(urls: List<String>): String? =
urls
.distinct()
.flatMap { url ->
getCookie(url)
?.split(";")
.orEmpty()
}.map { it.trim() }
.filter { it.isNotEmpty() }
.distinct()
.joinToString("; ")
.takeIf { it.isNotBlank() }

private fun String.hostOrNull(): String? = runCatching { Uri.parse(this).host }.getOrNull()
Loading
Loading