diff --git a/TERMS_OF_SERVICE_AND_PRIVACY_POLICY-zh.md b/TERMS_OF_SERVICE_AND_PRIVACY_POLICY-zh.md new file mode 100644 index 0000000..9c37578 --- /dev/null +++ b/TERMS_OF_SERVICE_AND_PRIVACY_POLICY-zh.md @@ -0,0 +1,28 @@ +# 服务条款与隐私政策 + +### 隐私政策 + +我非常重视您的隐私。**Read You** 不收集任何用户数据,所有敏感信息(密码和其他账户信息)都安全地存储在您设备上的本地应用数据库中。 + +**Read You** 将使用以下权限为您提供服务: + +- 访问网络权限(用于访问您所指定的在线内容) +- 获取网络状态权限(用于获取当前设备是否拥有可用的网络条件) +- 后台服务权限(用于定时自动地为您同步已收藏的内容) + +### 第三方服务 + +本政策不适用于您与 **Read You** 一起使用的第三方服务。您可以在所使用的第三方服务的网站上查阅相关的隐私政策。 + +### 免责声明 + +**Read You** 仅是一款内容收藏工具。您在使用 **Read You** 的过程中,应遵守所在国家和地区的法律法规,因您的行为所产生的一切责任将由您个人承担。 + +### 开源许可 + +**Read You** 是一个使用 GNU GPL 3.0 开源许可协议的开源项目 ①,允许您免费使用、引用、修改 **Read You** 的源代码,但不允许修改后和其衍生的代码作为闭源的商业软件进行发布和销售。具体细节请查看完整的 GNU GPL 3.0 开源许可协议 ②。 + +### 附录 + +1. [https://github.com/Ashinch/ReadYou](https://github.com/Ashinch/ReadYou) +2. [https://www.gnu.org/licenses/gpl-3.0.html](https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/TERMS_OF_SERVICE_AND_PRIVACY_POLICY.md b/TERMS_OF_SERVICE_AND_PRIVACY_POLICY.md new file mode 100644 index 0000000..0f7f034 --- /dev/null +++ b/TERMS_OF_SERVICE_AND_PRIVACY_POLICY.md @@ -0,0 +1,28 @@ +# Terms of Service and Privacy Policy + +### Privacy Policy + +I take your privacy very seriously. **Read You** does not collect any user data, and all sensitive information (passwords and other account information) is securely stored in the local application database on your device. + +**Read You** will use the following permissions to provide you with the service. + +- Access Network permission (for accessing online content as you specify) +- Get network status permission (for getting whether the device currently has available network conditions) +- Background service permission (to automatically sync your favorites for you on a regular basis) + +### Third Party Services + +This policy does not apply to third-party services that you use with **Read You**. You can review the privacy policies of the third-party services you use on their websites. + +### Disclaimers + +**Read You** is a content collection tool only. Your use of **Read You** is subject to the laws and regulations of your country and region, and any liability arising from your actions will be borne by you personally. + +### Open Source License + +**Read You** is an open source project under the GNU GPL 3.0 Open Source License ①, which allows you to use, reference, and modify the source code of **Read You** for free, but does not allow the modified and derived code to be distributed and sold as closed-source commercial software. For details, please see the full GNU GPL 3.0 Open Source License ②. + +### Appendix + +1. [https://github.com/Ashinch/ReadYou](https://github.com/Ashinch/ReadYou) +2. [https://www.gnu.org/licenses/gpl-3.0.html](https://www.gnu.org/licenses/gpl-3.0.html) diff --git a/app/build.gradle b/app/build.gradle index cc131fb..5a4e660 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "me.ash.reader" minSdk 26 targetSdk 32 - versionCode 1 - versionName "0.6.1" + versionCode 2 + versionName "0.6.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { @@ -53,6 +53,7 @@ android { } dependencies { + implementation 'com.github.ireward:compose-html:1.0.2' implementation 'be.ceau:opml-parser:2.2.0' implementation "androidx.profileinstaller:profileinstaller:1.2.0-alpha02" implementation("io.coil-kt:coil-compose:2.0.0-rc02") diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json index ec869aa..6e5dd5a 100644 --- a/app/release/output-metadata.json +++ b/app/release/output-metadata.json @@ -11,8 +11,8 @@ "type": "SINGLE", "filters": [], "attributes": [], - "versionCode": 1, - "versionName": "0.6.1", + "versionCode": 2, + "versionName": "0.6.2", "outputFile": "app-release.apk" } ], diff --git a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt index da4d35a..036c897 100644 --- a/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt +++ b/app/src/main/java/me/ash/reader/ui/ext/DataStoreExt.kt @@ -3,10 +3,7 @@ package me.ash.reader.ui.ext import android.content.Context import android.util.Log import androidx.datastore.core.DataStore -import androidx.datastore.preferences.core.Preferences -import androidx.datastore.preferences.core.edit -import androidx.datastore.preferences.core.emptyPreferences -import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.* import androidx.datastore.preferences.preferencesDataStore import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.first @@ -15,6 +12,8 @@ import kotlinx.coroutines.runBlocking import java.io.IOException val Context.dataStore: DataStore by preferencesDataStore(name = "settings") +val Context.isFirstLaunch: Boolean + get() = this.dataStore.get(DataStoreKeys.IsFirstLaunch) ?: true val Context.currentAccountId: Int get() = this.dataStore.get(DataStoreKeys.CurrentAccountId)!! val Context.currentAccountType: Int @@ -46,6 +45,11 @@ fun DataStore.get(dataStoreKeys: DataStoreKeys): T? { sealed class DataStoreKeys { abstract val key: Preferences.Key + object IsFirstLaunch : DataStoreKeys() { + override val key: Preferences.Key + get() = booleanPreferencesKey("isFirstLaunch") + } + object CurrentAccountId : DataStoreKeys() { override val key: Preferences.Key get() = intPreferencesKey("currentAccountId") diff --git a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt index 1e4bceb..8760800 100644 --- a/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt +++ b/app/src/main/java/me/ash/reader/ui/page/common/HomeEntry.kt @@ -11,6 +11,7 @@ import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext import com.google.accompanist.insets.ProvideWindowInsets import com.google.accompanist.insets.navigationBarsHeight import com.google.accompanist.insets.statusBarsPadding @@ -18,13 +19,16 @@ import com.google.accompanist.navigation.animation.AnimatedNavHost import com.google.accompanist.navigation.animation.rememberAnimatedNavController import com.google.accompanist.systemuicontroller.rememberSystemUiController import me.ash.reader.ui.ext.animatedComposable +import me.ash.reader.ui.ext.isFirstLaunch import me.ash.reader.ui.page.home.HomePage import me.ash.reader.ui.page.settings.SettingsPage +import me.ash.reader.ui.page.startup.StartupPage import me.ash.reader.ui.theme.AppTheme @OptIn(ExperimentalAnimationApi::class, androidx.compose.material.ExperimentalMaterialApi::class) @Composable fun HomeEntry() { + val context = LocalContext.current val navController = rememberAnimatedNavController() AppTheme { @@ -42,8 +46,11 @@ fun HomeEntry() { ) { AnimatedNavHost( navController = navController, - startDestination = RouteName.HOME, + startDestination = if (context.isFirstLaunch) RouteName.STARTUP else RouteName.HOME, ) { + animatedComposable(route = RouteName.STARTUP) { + StartupPage(navController) + } animatedComposable(route = RouteName.HOME) { HomePage(navController) } diff --git a/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt b/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt index 05a0d2c..96bf4b3 100644 --- a/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt +++ b/app/src/main/java/me/ash/reader/ui/page/common/RouteName.kt @@ -1,6 +1,7 @@ package me.ash.reader.ui.page.common object RouteName { + const val STARTUP = "startup" const val HOME = "home" const val FEED = "feed" const val ARTICLE = "article" diff --git a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt index 724947a..06209db 100644 --- a/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt +++ b/app/src/main/java/me/ash/reader/ui/page/settings/SettingsPage.kt @@ -1,7 +1,8 @@ package me.ash.reader.ui.page.settings import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.height import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.* @@ -9,22 +10,15 @@ import androidx.compose.material.icons.rounded.ArrowBack import androidx.compose.material.icons.rounded.Close import androidx.compose.material3.* import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource -import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp -import androidx.compose.ui.unit.sp import androidx.navigation.NavHostController import me.ash.reader.R import me.ash.reader.ui.component.Banner import me.ash.reader.ui.component.DisplayText import me.ash.reader.ui.component.FeedbackIconButton import me.ash.reader.ui.component.SelectableSettingGroupItem -import me.ash.reader.ui.ext.paddingFixedHorizontal -import me.ash.reader.ui.ext.roundClick import me.ash.reader.ui.page.common.RouteName @OptIn(ExperimentalMaterial3Api::class) @@ -108,48 +102,3 @@ fun SettingsPage( } ) } - -@Composable -fun SettingsItem( - title: String = "", - description: String = "", - imageVector: ImageVector, -) { - Row(modifier = Modifier - .fillMaxWidth() - .roundClick { } - ) { - - Row( - modifier = Modifier.paddingFixedHorizontal(top = 16.dp, bottom = 16.dp), - verticalAlignment = Alignment.CenterVertically - ) { - Spacer(modifier = Modifier.width(4.dp)) - Icon( - imageVector = imageVector, - contentDescription = title, - tint = MaterialTheme.colorScheme.onPrimaryContainer, - ) - Spacer(modifier = Modifier.width(20.dp)) - Column { - Text( - text = title, - fontSize = 20.sp, - color = MaterialTheme.colorScheme.onPrimaryContainer, - ) - Text( - text = description, - color = MaterialTheme.colorScheme.outline, - ) - } - } - } -} - -@Preview -@Composable -fun SettingsPreview() { - Row(modifier = Modifier.background(MaterialTheme.colorScheme.surface)) { - SettingsPage(navController = NavHostController(LocalContext.current)) - } -} \ No newline at end of file diff --git a/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt b/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt new file mode 100644 index 0000000..db92d1e --- /dev/null +++ b/app/src/main/java/me/ash/reader/ui/page/startup/StartupPage.kt @@ -0,0 +1,137 @@ +package me.ash.reader.ui.page.startup + +import android.content.Intent +import android.net.Uri +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.Info +import androidx.compose.material.icons.rounded.CheckCircleOutline +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import androidx.navigation.NavHostController +import com.ireward.htmlcompose.HtmlText +import kotlinx.coroutines.launch +import me.ash.reader.R +import me.ash.reader.ui.component.DisplayText +import me.ash.reader.ui.ext.DataStoreKeys +import me.ash.reader.ui.ext.dataStore +import me.ash.reader.ui.ext.put +import me.ash.reader.ui.page.common.RouteName + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun StartupPage( + navController: NavHostController, +) { + val context = LocalContext.current + val scope = rememberCoroutineScope() + + Scaffold( + modifier = Modifier.background(MaterialTheme.colorScheme.surface), + topBar = {}, + content = { + LazyColumn { + item { + Spacer(modifier = Modifier.height(64.dp)) + DisplayText(text = stringResource(R.string.welcome), desc = "") + } + item { + Spacer(modifier = Modifier.height(16.dp)) + Image( + modifier = Modifier.padding(horizontal = 16.dp), + painter = painterResource(id = R.drawable.welcome), + contentDescription = stringResource(R.string.welcome), + ) + } + item { + TipsItem( + modifier = Modifier + .padding(horizontal = 24.dp) + .padding(top = 40.dp) + ) + } + item { + Spacer(modifier = Modifier.height(10.dp)) + TextButton( + modifier = Modifier.padding(horizontal = 12.dp), + onClick = { + context.let { + it.startActivity( + Intent( + Intent.ACTION_VIEW, + Uri.parse(it.getString(R.string.terms_link)) + ) + ) + } + } + ) { + HtmlText( + text = stringResource(R.string.view_terms), + style = MaterialTheme.typography.bodySmall.copy( + color = MaterialTheme.colorScheme.outline, + ), + ) + } + Spacer(modifier = Modifier.height(100.dp)) + } + } + }, + bottomBar = { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(24.dp), + horizontalArrangement = Arrangement.End, + verticalAlignment = Alignment.CenterVertically, + ) { + ExtendedFloatingActionButton( + onClick = { + navController.navigate(route = RouteName.HOME) + scope.launch { + context.dataStore.put(DataStoreKeys.IsFirstLaunch, false) + } + }, + icon = { + Icon( + Icons.Rounded.CheckCircleOutline, + stringResource(R.string.agree_and_continue) + ) + }, + text = { Text(text = stringResource(R.string.agree_and_continue)) }, + ) + } + } + ) +} + +@Composable +private fun TipsItem( + modifier: Modifier = Modifier, +) { + Column( + modifier = modifier + .fillMaxWidth() + ) { + Icon( + imageVector = Icons.Outlined.Info, + contentDescription = stringResource(R.string.tips_and_support), + tint = MaterialTheme.colorScheme.onSurfaceVariant, + ) + Spacer(modifier = Modifier.height(22.dp)) + Text( + text = stringResource(R.string.agree_terms), + style = MaterialTheme.typography.titleSmall, + color = MaterialTheme.colorScheme.onSurfaceVariant, + ) + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/welcome.xml b/app/src/main/res/drawable/welcome.xml new file mode 100644 index 0000000..7d38fe4 --- /dev/null +++ b/app/src/main/res/drawable/welcome.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index bc090f2..b011f0a 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -77,4 +77,9 @@ 英语、中文 提示和支持 关于、开源 + 欢迎 + 在此之前,您需要同意 Read You 的服务条款与隐私政策后才能继续。 + 查看《<u>服务条款与隐私政策</u>》 + https://gitee.com/Ashinch/ReadYou/blob/main/TERMS_OF_SERVICE_AND_PRIVACY_POLICY-zh.md + 同意并继续 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index fe04280..d861067 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -77,4 +77,9 @@ English, Chinese Tips & support About, open source + Welcome + Before you can continue, you need to agree to Read You\'s Terms of Service and Privacy Policy. + View the <i><u>Terms of Service and Privacy Policy</u></i> + https://github.com/Ashinch/ReadYou/blob/main/TERMS_OF_SERVICE_AND_PRIVACY_POLICY.md + Agree and Continue \ No newline at end of file