Fix fading edge

This commit is contained in:
andrew 2023-08-27 00:12:20 +09:30
parent 71b2544c31
commit 7a705642a2
5 changed files with 94 additions and 50 deletions

View File

@ -166,14 +166,14 @@ dependencies {
testImplementation 'org.robolectric:shadows-multidex:4.4' testImplementation 'org.robolectric:shadows-multidex:4.4'
implementation 'com.github.bumptech.glide:compose:1.0.0-alpha.1' implementation 'com.github.bumptech.glide:compose:1.0.0-alpha.1'
implementation 'androidx.compose.ui:ui:1.5.0' implementation 'androidx.compose.ui:ui:1.4.3'
implementation 'androidx.compose.ui:ui-tooling:1.5.0' implementation 'androidx.compose.ui:ui-tooling:1.4.3'
implementation "com.google.accompanist:accompanist-themeadapter-appcompat:0.31.5-beta" implementation "com.google.accompanist:accompanist-themeadapter-appcompat:0.31.5-beta"
implementation "com.google.accompanist:accompanist-pager-indicators:0.31.5-beta" implementation "com.google.accompanist:accompanist-pager-indicators:0.31.5-beta"
implementation "androidx.compose.runtime:runtime-livedata:1.5.0" implementation "androidx.compose.runtime:runtime-livedata:1.4.3"
implementation 'androidx.compose.foundation:foundation-layout:1.5.0' implementation 'androidx.compose.foundation:foundation-layout:1.4.3'
implementation 'androidx.compose.material:material:1.5.0' implementation 'androidx.compose.material:material:1.4.3'
} }
def canonicalVersionCode = 354 def canonicalVersionCode = 354

View File

@ -4,11 +4,13 @@ import android.os.Bundle
import android.widget.Toast import android.widget.Toast
import androidx.activity.viewModels import androidx.activity.viewModels
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn import androidx.compose.foundation.layout.heightIn
@ -16,6 +18,7 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll import androidx.compose.foundation.verticalScroll
@ -29,8 +32,14 @@ import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawWithContent
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.BlendMode
import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.CompositingStrategy
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.layout.ModifierLocalBeyondBoundsLayout
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
@ -48,11 +57,13 @@ import org.thoughtcrime.securesms.database.RecipientDatabase
import org.thoughtcrime.securesms.database.ThreadDatabase import org.thoughtcrime.securesms.database.ThreadDatabase
import org.thoughtcrime.securesms.ui.AppTheme import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.CellNoMargin import org.thoughtcrime.securesms.ui.CellNoMargin
import org.thoughtcrime.securesms.ui.Divider
import org.thoughtcrime.securesms.ui.GetString import org.thoughtcrime.securesms.ui.GetString
import org.thoughtcrime.securesms.ui.PreviewTheme import org.thoughtcrime.securesms.ui.PreviewTheme
import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider import org.thoughtcrime.securesms.ui.ThemeResPreviewParameterProvider
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.min
@AndroidEntryPoint @AndroidEntryPoint
class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() { class ExpirationSettingsActivity: PassphraseRequiredActionBarActivity() {
@ -189,18 +200,22 @@ fun DisappearingMessages(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
onSetClick: () -> Unit = {} onSetClick: () -> Unit = {}
) { ) {
val scrollState = rememberScrollState()
Column(modifier = modifier) { Column(modifier = modifier) {
Box(modifier = Modifier.weight(1f)) { Box(modifier = Modifier.weight(1f)) {
Column( Column(
modifier = Modifier modifier = Modifier
.padding(horizontal = 32.dp) .padding(horizontal = 32.dp)
.verticalScroll(rememberScrollState()), .padding(bottom = 20.dp)
.verticalScroll(scrollState)
.fadingEdges(scrollState),
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
state.cards.filter { it.options.isNotEmpty() }.forEach { OptionsCard(it) } state.cards.filter { it.options.isNotEmpty() }.forEach {
OptionsCard(it)
}
} }
Gradient(100.dp, modifier = Modifier.align(Alignment.BottomCenter))
} }
OutlineButton( OutlineButton(
@ -213,6 +228,43 @@ fun DisappearingMessages(
} }
} }
fun Modifier.fadingEdges(
scrollState: ScrollState,
topEdgeHeight: Dp = 0.dp,
bottomEdgeHeight: Dp = 20.dp
): Modifier = this.then(
Modifier
// adding layer fixes issue with blending gradient and content
.graphicsLayer { alpha = 0.99F }
.drawWithContent {
drawContent()
val topColors = listOf(Color.Transparent, Color.Black)
val topStartY = scrollState.value.toFloat()
val topGradientHeight = min(topEdgeHeight.toPx(), topStartY)
drawRect(
brush = Brush.verticalGradient(
colors = topColors,
startY = topStartY,
endY = topStartY + topGradientHeight
),
blendMode = BlendMode.DstIn
)
val bottomColors = listOf(Color.Black, Color.Transparent)
val bottomEndY = size.height - scrollState.maxValue + scrollState.value
val bottomGradientHeight = min(bottomEdgeHeight.toPx(), scrollState.maxValue.toFloat() - scrollState.value)
if (bottomGradientHeight != 0f) drawRect(
brush = Brush.verticalGradient(
colors = bottomColors,
startY = bottomEndY - bottomGradientHeight,
endY = bottomEndY
),
blendMode = BlendMode.DstIn
)
}
)
@Composable @Composable
fun OptionsCard(card: CardModel) { fun OptionsCard(card: CardModel) {
Text(text = card.title()) Text(text = card.title())
@ -220,7 +272,8 @@ fun OptionsCard(card: CardModel) {
LazyColumn( LazyColumn(
modifier = Modifier.heightIn(max = 5000.dp) modifier = Modifier.heightIn(max = 5000.dp)
) { ) {
items(card.options) { itemsIndexed(card.options) { i, it ->
if (i != 0) Divider()
TitledRadioButton(it) TitledRadioButton(it)
} }
} }
@ -233,13 +286,13 @@ fun Gradient(height: Dp, modifier: Modifier = Modifier) {
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.height(height) .height(height)
.background( // .background(
brush = Brush.verticalGradient( // brush = Brush.verticalGradient(
colors = listOf(Color.Transparent, MaterialTheme.colors.primary), // colors = listOf(Color.Transparent, MaterialTheme.colors.primary),
startY = 0f, // startY = 0f,
endY = height.value // endY = height.value
) // )
) // )
) )
} }

View File

@ -90,15 +90,15 @@ class ExpirationSettingsViewModel(
fun typeOption( fun typeOption(
type: ExpiryType, type: ExpiryType,
@StringRes title: Int, @StringRes title: Int,
@StringRes subtitle: Int, @StringRes subtitle: Int? = null,
// @StringRes contentDescription: Int @StringRes contentDescription: Int = title
) = OptionModel(GetString(title), GetString(subtitle)) { setType(type) } ) = OptionModel(GetString(title), subtitle?.let(::GetString)) { setType(type) }
private fun typeOptions() = listOf( private fun typeOptions() = listOf(
typeOption(ExpiryType.NONE, R.string.expiration_off, R.string.AccessibilityId_disable_disappearing_messages), typeOption(ExpiryType.NONE, R.string.expiration_off, contentDescription = R.string.AccessibilityId_disable_disappearing_messages),
typeOption(ExpiryType.LEGACY, R.string.expiration_type_disappear_legacy, R.string.expiration_type_disappear_legacy_description), typeOption(ExpiryType.LEGACY, R.string.expiration_type_disappear_legacy, contentDescription = R.string.expiration_type_disappear_legacy_description),
typeOption(ExpiryType.AFTER_READ, R.string.expiration_type_disappear_after_read, R.string.expiration_type_disappear_after_read_description), typeOption(ExpiryType.AFTER_READ, R.string.expiration_type_disappear_after_read, contentDescription = R.string.expiration_type_disappear_after_read_description),
typeOption(ExpiryType.AFTER_SEND, R.string.expiration_type_disappear_after_send, R.string.expiration_type_disappear_after_send_description), typeOption(ExpiryType.AFTER_SEND, R.string.expiration_type_disappear_after_send, contentDescription = R.string.expiration_type_disappear_after_send_description),
) )
private fun setType(type: ExpiryType) { private fun setType(type: ExpiryType) {
@ -127,7 +127,7 @@ class ExpirationSettingsViewModel(
val afterSendTimes = listOf(5.minutes, 1.hours) + afterReadTimes val afterSendTimes = listOf(5.minutes, 1.hours) + afterReadTimes
private fun noteToSelfOptions() = listOfNotNull( private fun noteToSelfOptions() = listOfNotNull(
typeOption(ExpiryType.NONE, R.string.arrays__off, R.string.arrays__off), typeOption(ExpiryType.NONE, R.string.arrays__off),
noteToSelfOption(1.minutes, subtitle = "for testing purposes").takeIf { BuildConfig.DEBUG }, noteToSelfOption(1.minutes, subtitle = "for testing purposes").takeIf { BuildConfig.DEBUG },
) + afterSendTimes.map(::noteToSelfOption) ) + afterSendTimes.map(::noteToSelfOption)

View File

@ -1,33 +1,24 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scrollView"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:fillViewport="true"> android:orientation="vertical">
<RelativeLayout <androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:clipChildren="false"> android:background="?colorPrimary"
app:contentInsetStart="0dp"
app:subtitle="@string/activity_expiration_settings_subtitle"
app:subtitleTextAppearance="@style/TextAppearance.Session.ToolbarSubtitle"
app:title="@string/activity_expiration_settings_title" />
<androidx.appcompat.widget.Toolbar <androidx.compose.ui.platform.ComposeView
android:id="@+id/toolbar" android:id="@+id/container"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="match_parent" />
android:background="?colorPrimary"
app:contentInsetStart="0dp"
app:subtitle="@string/activity_expiration_settings_subtitle"
app:subtitleTextAppearance="@style/TextAppearance.Session.ToolbarSubtitle"
app:title="@string/activity_expiration_settings_title" />
<androidx.compose.ui.platform.ComposeView </LinearLayout>
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@id/toolbar"/>
</RelativeLayout>
</ScrollView>

View File

@ -57,6 +57,6 @@ allprojects {
project.ext { project.ext {
androidMinimumSdkVersion = 23 androidMinimumSdkVersion = 23
androidTargetSdkVersion = 33 androidTargetSdkVersion = 33
androidCompileSdkVersion = 34 androidCompileSdkVersion = 33
} }
} }