Add Tips and Support page UI

This commit is contained in:
Ash 2022-04-22 23:09:34 +08:00
parent 7d0721e80a
commit 4b1912310e
13 changed files with 424 additions and 18 deletions

View File

@ -0,0 +1,83 @@
package me.ash.reader.ui.component
import androidx.compose.foundation.shape.CornerBasedShape
import androidx.compose.foundation.shape.CornerSize
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.shape.ZeroCornerSize
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Outline
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.unit.LayoutDirection
import kotlin.math.cos
import kotlin.math.sin
val curlyCornerShape = CurlyCornerShape()
class CurlyCornerShape(
private val amp: Double = 16.0,
private val count: Int = 12,
) : CornerBasedShape(
topStart = ZeroCornerSize,
topEnd = ZeroCornerSize,
bottomEnd = ZeroCornerSize,
bottomStart = ZeroCornerSize
) {
private fun sineCircleXYatAngle(
d1: Double,
d2: Double,
d3: Double,
d4: Double,
d5: Double,
i: Int
): List<Double> = (i.toDouble() * d5).run {
listOf(
(sin(this) * d4 + d3) * cos(d5) + d1,
(sin(this) * d4 + d3) * sin(d5) + d2
)
}
override fun createOutline(
size: Size,
topStart: Float,
topEnd: Float,
bottomEnd: Float,
bottomStart: Float,
layoutDirection: LayoutDirection
): Outline {
val d = 2.0
val r2: Double = size.width / d
var r13: Double = size.height / d
val r18 = size.width / 2.0 - amp
val path = Path()
path.moveTo((d * r2 - amp).toFloat(), r13.toFloat())
var i = 0
while (true) {
val i2 = i + 1
val d3 = r13
val r5: List<Double> = sineCircleXYatAngle(
r2, r13, r18, amp, Math.toRadians(
i.toDouble()
), count
)
path.lineTo(r5[0].toFloat(), r5[1].toFloat())
if (i2 >= 360) {
path.close()
return Outline.Generic(path)
}
i = i2
r13 = d3
}
}
override fun copy(
topStart: CornerSize,
topEnd: CornerSize,
bottomEnd: CornerSize,
bottomStart: CornerSize
) = RoundedCornerShape(
topStart = topStart,
topEnd = topEnd,
bottomEnd = bottomEnd,
bottomStart = bottomStart
)
}

View File

@ -15,6 +15,7 @@ import me.ash.reader.ui.ext.isFirstLaunch
import me.ash.reader.ui.page.home.HomePage
import me.ash.reader.ui.page.settings.ColorAndStyle
import me.ash.reader.ui.page.settings.SettingsPage
import me.ash.reader.ui.page.settings.TipsAndSupport
import me.ash.reader.ui.page.startup.StartupPage
import me.ash.reader.ui.theme.AppTheme
import me.ash.reader.ui.theme.LocalUseDarkTheme
@ -50,6 +51,9 @@ fun HomeEntry() {
animatedComposable(route = RouteName.COLOR_AND_STYLE) {
ColorAndStyle(navController)
}
animatedComposable(route = RouteName.TIPS_AND_SUPPORT) {
TipsAndSupport(navController)
}
}
}
}

View File

@ -8,4 +8,5 @@ object RouteName {
const val READ = "read"
const val SETTINGS = "settings"
const val COLOR_AND_STYLE = "color_and_style"
const val TIPS_AND_SUPPORT = "tips_and_support"
}

View File

@ -13,9 +13,11 @@ import me.ash.reader.ui.component.ViewPager
import me.ash.reader.ui.ext.collectAsStateValue
import me.ash.reader.ui.ext.findActivity
import me.ash.reader.ui.page.common.ExtraName
import me.ash.reader.ui.page.home.feeds.FeedsPage
import me.ash.reader.ui.page.home.feeds.option.feed.FeedOptionDrawer
import me.ash.reader.ui.page.home.feeds.option.feed.FeedOptionViewAction
import me.ash.reader.ui.page.home.feeds.option.feed.FeedOptionViewModel
import me.ash.reader.ui.page.home.feeds.FeedsPage
import me.ash.reader.ui.page.home.feeds.option.group.GroupOptionDrawer
import me.ash.reader.ui.page.home.flow.FlowPage
import me.ash.reader.ui.page.home.read.ReadPage
import me.ash.reader.ui.page.home.read.ReadViewAction
@ -148,4 +150,7 @@ fun HomePage(
),
)
}
FeedOptionDrawer()
GroupOptionDrawer()
}

View File

@ -38,8 +38,6 @@ import me.ash.reader.ui.ext.getName
import me.ash.reader.ui.page.common.RouteName
import me.ash.reader.ui.page.home.FilterBar
import me.ash.reader.ui.page.home.FilterState
import me.ash.reader.ui.page.home.feeds.option.feed.FeedOptionDrawer
import me.ash.reader.ui.page.home.feeds.option.group.GroupOptionDrawer
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeDialog
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewAction
import me.ash.reader.ui.page.home.feeds.subscribe.SubscribeViewModel
@ -241,8 +239,5 @@ fun FeedsPage(
)
}
)
FeedOptionDrawer()
GroupOptionDrawer()
}

View File

@ -186,7 +186,9 @@ private fun AddToGroup(
Spacer(modifier = Modifier.height(10.dp))
if (groups.size > 6) {
LazyRow {
LazyRow(
verticalAlignment = Alignment.CenterVertically,
) {
items(groups) {
SelectionChip(
modifier = Modifier.animateContentSize(),

View File

@ -107,8 +107,9 @@ fun SettingsPage(
title = stringResource(R.string.tips_and_support),
desc = stringResource(R.string.tips_and_support_desc),
icon = Icons.Outlined.TipsAndUpdates,
enable = false,
) {}
) {
navController.navigate(RouteName.TIPS_AND_SUPPORT)
}
}
}
}

View File

@ -0,0 +1,294 @@
package me.ash.reader.ui.page.settings
import android.content.Intent
import android.net.Uri
import android.view.HapticFeedbackConstants
import android.view.SoundEffectConstants
import androidx.compose.animation.animateContentSize
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectTapGestures
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.ArrowBack
import androidx.compose.material.icons.rounded.Balance
import androidx.compose.material.icons.rounded.VolunteerActivism
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import me.ash.reader.R
import me.ash.reader.ui.component.CurlyCornerShape
import me.ash.reader.ui.component.FeedbackIconButton
import me.ash.reader.ui.theme.palette.alwaysLight
import me.ash.reader.ui.theme.palette.onLight
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun TipsAndSupport(
navController: NavHostController,
) {
val context = LocalContext.current
val view = LocalView.current
val githubLink = stringResource(R.string.github_link)
val telegramLink = stringResource(R.string.telegram_link)
var version by remember { mutableStateOf("") }
var pressAMP by remember { mutableStateOf(16f) }
val animatedPress by animateFloatAsState(
targetValue = pressAMP,
animationSpec = tween()
)
LaunchedEffect(Unit) {
version = context.packageManager.getPackageInfo(context.packageName, 0).versionName
}
Scaffold(
modifier = Modifier
.background(MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface)
.statusBarsPadding()
.navigationBarsPadding(),
containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface,
topBar = {
SmallTopAppBar(
colors = TopAppBarDefaults.smallTopAppBarColors(
containerColor = MaterialTheme.colorScheme.surface onLight MaterialTheme.colorScheme.inverseOnSurface
),
title = {},
navigationIcon = {
FeedbackIconButton(
imageVector = Icons.Rounded.ArrowBack,
contentDescription = stringResource(R.string.back),
tint = MaterialTheme.colorScheme.onSurface
) {
navController.popBackStack()
}
},
actions = {}
)
},
content = {
LazyColumn(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceAround,
) {
item {
Column(
modifier = Modifier.pointerInput(Unit) {
detectTapGestures(
onPress = {
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
pressAMP = 0f
tryAwaitRelease()
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
view.playSoundEffect(SoundEffectConstants.CLICK)
pressAMP = 16f
},
)
},
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Box(
modifier = Modifier
.size(240.dp)
.background(
color = MaterialTheme.colorScheme.primaryContainer alwaysLight true,
shape = CurlyCornerShape(amp = animatedPress.toDouble()),
)
.shadow(
elevation = 10.dp,
shape = CurlyCornerShape(amp = animatedPress.toDouble()),
ambientColor = MaterialTheme.colorScheme.primaryContainer alwaysLight true,
spotColor = MaterialTheme.colorScheme.primaryContainer alwaysLight true,
),
contentAlignment = Alignment.Center,
) {
Image(
modifier = Modifier.size(90.dp),
painter = painterResource(R.drawable.ic_launcher_monochrome),
contentDescription = stringResource(R.string.read_you),
colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onSurface alwaysLight true),
)
}
Spacer(modifier = Modifier.height(48.dp))
BadgedBox(
badge = {
Badge(
modifier = Modifier.animateContentSize(tween(800)),
containerColor = MaterialTheme.colorScheme.tertiaryContainer,
contentColor = MaterialTheme.colorScheme.tertiary,
) {
Text(text = version)
}
}
) {
Text(
text = stringResource(R.string.read_you),
style = MaterialTheme.typography.displaySmall
)
}
}
}
item {
Spacer(modifier = Modifier.height(48.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Center,
verticalAlignment = Alignment.CenterVertically,
) {
// Sponsor
RoundIconButton(RoundIconButtonType.Sponsor(
backgroundColor = MaterialTheme.colorScheme.tertiaryContainer alwaysLight true,
) {})
Spacer(modifier = Modifier.width(16.dp))
// Telegram
RoundIconButton(RoundIconButtonType.Telegram(
backgroundColor = MaterialTheme.colorScheme.primaryContainer alwaysLight true,
) {
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
view.playSoundEffect(SoundEffectConstants.CLICK)
context.startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse(telegramLink)
)
)
})
Spacer(modifier = Modifier.width(16.dp))
// GitHub
RoundIconButton(RoundIconButtonType.GitHub(
backgroundColor = MaterialTheme.colorScheme.primaryContainer alwaysLight true,
) {
view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP)
view.playSoundEffect(SoundEffectConstants.CLICK)
context.startActivity(
Intent(
Intent.ACTION_VIEW,
Uri.parse(githubLink)
)
)
})
Spacer(modifier = Modifier.width(16.dp))
// License
RoundIconButton(RoundIconButtonType.License(
backgroundColor = MaterialTheme.colorScheme.secondaryContainer alwaysLight true,
) {})
}
Spacer(modifier = Modifier.height(48.dp))
}
}
}
)
}
@Immutable
sealed class RoundIconButtonType(
val iconResource: Int? = null,
val iconVector: ImageVector? = null,
val descResource: Int? = null,
val descString: String? = null,
open val offset: Modifier = Modifier.offset(),
open val backgroundColor: Color = Color.Unspecified,
open val onClick: () -> Unit = {},
) {
@Immutable
data class Sponsor(
val desc: Int = R.string.sponsor,
override val backgroundColor: Color,
override val onClick: () -> Unit = {},
) : RoundIconButtonType(
iconVector = Icons.Rounded.VolunteerActivism,
descResource = desc,
backgroundColor = backgroundColor,
onClick = onClick,
)
@Immutable
data class Telegram(
val desc: String = "Telegram",
override val offset: Modifier = Modifier.offset(x = (-1).dp),
override val backgroundColor: Color,
override val onClick: () -> Unit = {},
) : RoundIconButtonType(
iconResource = R.drawable.ic_telegram,
descString = desc,
backgroundColor = backgroundColor,
onClick = onClick,
)
@Immutable
data class GitHub(
val desc: String = "GitHub",
override val backgroundColor: Color,
override val onClick: () -> Unit = {},
) : RoundIconButtonType(
iconResource = R.drawable.ic_github,
descString = desc,
backgroundColor = backgroundColor,
onClick = onClick,
)
@Immutable
data class License(
val desc: Int = R.string.open_source_licenses,
override val backgroundColor: Color,
override val onClick: () -> Unit = {},
) : RoundIconButtonType(
iconVector = Icons.Rounded.Balance,
descResource = desc,
backgroundColor = backgroundColor,
onClick = onClick,
)
}
@Composable
private fun RoundIconButton(type: RoundIconButtonType) {
IconButton(
modifier = Modifier
.size(70.dp)
.background(
color = type.backgroundColor,
shape = CircleShape,
),
onClick = { type.onClick() }
) {
when (type) {
is RoundIconButtonType.Sponsor, is RoundIconButtonType.License -> {
Icon(
modifier = type.offset,
imageVector = type.iconVector!!,
contentDescription = stringResource(type.descResource!!),
tint = MaterialTheme.colorScheme.onSurface alwaysLight true,
)
}
is RoundIconButtonType.GitHub, is RoundIconButtonType.Telegram -> {
Icon(
modifier = type.offset,
painter = painterResource(type.iconResource!!),
contentDescription = type.descString,
tint = MaterialTheme.colorScheme.onSurface alwaysLight true,
)
}
}
}
}

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M12,0c-6.626,0 -12,5.373 -12,12 0,5.302 3.438,9.8 8.207,11.387 0.599,0.111 0.793,-0.261 0.793,-0.577v-2.234c-3.338,0.726 -4.033,-1.416 -4.033,-1.416 -0.546,-1.387 -1.333,-1.756 -1.333,-1.756 -1.089,-0.745 0.083,-0.729 0.083,-0.729 1.205,0.084 1.839,1.237 1.839,1.237 1.07,1.834 2.807,1.304 3.492,0.997 0.107,-0.775 0.418,-1.305 0.762,-1.604 -2.665,-0.305 -5.467,-1.334 -5.467,-5.931 0,-1.311 0.469,-2.381 1.236,-3.221 -0.124,-0.303 -0.535,-1.524 0.117,-3.176 0,0 1.008,-0.322 3.301,1.23 0.957,-0.266 1.983,-0.399 3.003,-0.404 1.02,0.005 2.047,0.138 3.006,0.404 2.291,-1.552 3.297,-1.23 3.297,-1.23 0.653,1.653 0.242,2.874 0.118,3.176 0.77,0.84 1.235,1.911 1.235,3.221 0,4.609 -2.807,5.624 -5.479,5.921 0.43,0.372 0.823,1.102 0.823,2.222v3.293c0,0.319 0.192,0.694 0.801,0.576 4.765,-1.589 8.199,-6.086 8.199,-11.386 0,-6.627 -5.373,-12 -12,-12z"/>
</vector>

View File

@ -2,26 +2,21 @@
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="#FFFFFF">
android:viewportHeight="24">
<group android:scaleX="0.14867486"
android:scaleY="0.14867486"
android:translateX="1.8901099"
android:translateY="2.410472">
<path
android:pathData="M32.502,81.24L68.244,107.141L35.572,126.763C29.589,130.373 22.267,125.036 23.841,118.286L32.502,81.24Z"
android:strokeAlpha="0.25"
android:fillColor="#191C1E"
android:fillAlpha="0.25"/>
android:fillAlpha="0.45"/>
<path
android:pathData="M132.739,57.079L103.846,82.038L45.981,40.047L60.861,5.12C63.537,-1.238 72.67,-1.238 75.346,5.12L90.226,40.125L128.252,43.343C135.18,43.893 138.014,52.527 132.739,57.079Z"
android:strokeAlpha="0.5"
android:fillColor="#191C1E"
android:fillAlpha="0.5"/>
android:fillAlpha="0.7"/>
<path
android:pathData="M100.916,127.188L68.244,107.487L32.501,81.586L3.608,56.627C-1.667,52.075 1.168,43.441 8.096,42.891L46.121,39.673L103.987,81.664L112.647,118.711C114.221,125.461 106.899,130.798 100.916,127.188Z"
android:strokeAlpha="0.8"
android:fillColor="#191C1E"
android:fillAlpha="0.8"/>
android:fillColor="#191C1E"/>
</group>
</vector>

View File

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="50"
android:viewportHeight="50">
<path
android:pathData="M46.137,6.552c-0.75,-0.636 -1.928,-0.727 -3.146,-0.238l-0.002,0C41.708,6.828 6.728,21.832 5.304,22.445c-0.259,0.09 -2.521,0.934 -2.288,2.814c0.208,1.695 2.026,2.397 2.248,2.478l8.893,3.045c0.59,1.964 2.765,9.21 3.246,10.758c0.3,0.965 0.789,2.233 1.646,2.494c0.752,0.29 1.5,0.025 1.984,-0.355l5.437,-5.043l8.777,6.845l0.209,0.125c0.596,0.264 1.167,0.396 1.712,0.396c0.421,0 0.825,-0.079 1.211,-0.237c1.315,-0.54 1.841,-1.793 1.896,-1.935l6.556,-34.077C47.231,7.933 46.675,7.007 46.137,6.552zM22,32l-3,8l-3,-10l23,-17L22,32z"
android:fillColor="#000000"/>
</vector>

View File

@ -107,4 +107,8 @@
<string name="feeds_page">订阅源页面</string>
<string name="flow_page">信息流页面</string>
<string name="reading_page">阅读页面</string>
<string name="sponsor">捐赠</string>
<string name="open_source_licenses">开放源代码许可</string>
<string name="github_link">https://github.com/Ashinch/ReadYou</string>
<string name="telegram_link">https://t.me/ReadYouApp</string>
</resources>

View File

@ -107,4 +107,8 @@
<string name="feeds_page">Feeds Page</string>
<string name="flow_page">Flow Page</string>
<string name="reading_page">Reading Page</string>
<string name="sponsor">Sponsor</string>
<string name="open_source_licenses">Open Source Licenses</string>
<string name="github_link">https://github.com/Ashinch/ReadYou</string>
<string name="telegram_link">https://t.me/ReadYouApp</string>
</resources>