更新
@@ -62,7 +62,7 @@ kotlin {
|
||||
implementation(project.dependencies.platform(libs.firebase.bom))
|
||||
implementation(libs.firebase.analytics)
|
||||
// facebook
|
||||
implementation(libs.android.facebook.android.sdk)
|
||||
// implementation(libs.android.facebook.android.sdk)
|
||||
|
||||
// mmkv
|
||||
implementation(libs.android.mmkv)
|
||||
@@ -143,8 +143,8 @@ android {
|
||||
applicationId = "com.taskttl"
|
||||
minSdk = libs.versions.android.minSdk.get().toInt()
|
||||
targetSdk = libs.versions.android.targetSdk.get().toInt()
|
||||
versionCode = 1
|
||||
versionName = "1.0"
|
||||
versionCode = libs.versions.android.versionCode.get().toInt()
|
||||
versionName = libs.versions.android.versionName.get().toString()
|
||||
|
||||
buildConfigField("String", "APP_NAME", "\"TaskTTL\"")
|
||||
|
||||
|
BIN
composeApp/release/composeApp-release.aab
Normal file
@@ -15,8 +15,6 @@
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:usesCleartextTraffic="true"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@android:style/Theme.Material.Light.NoActionBar">
|
||||
<activity
|
||||
android:exported="true"
|
||||
@@ -27,15 +25,33 @@
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter android:autoVerify="true">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
|
||||
<data
|
||||
android:host="dl.taskttl.com"
|
||||
android:pathPrefix="/"
|
||||
android:scheme="https" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<!-- Facebook -->
|
||||
<meta-data
|
||||
android:name="com.facebook.sdk.ApplicationId"
|
||||
android:value="${facebookAppId}" />
|
||||
<meta-data
|
||||
android:name="com.facebook.sdk.ClientToken"
|
||||
android:value="${facebookClientToken}" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
<queries>
|
||||
<!--<!– TikTok 国际版 –>-->
|
||||
<!--<package android:name="com.zhiliaoapp.musically" />-->
|
||||
<!--<!– TikTok 部分地区版本 –>-->
|
||||
<!--<package android:name="com.ss.android.ugc.trill" />-->
|
||||
<!--<!– 抖音(中国版) –>-->
|
||||
<!--<package android:name="com.ss.android.ugc.aweme" />-->
|
||||
<!-- 用于打开网页链接 -->
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<data android:scheme="https" />
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
|
@@ -67,7 +67,7 @@ actual object DeviceUtils {
|
||||
uniqueId = getUniqueId(),
|
||||
deviceInfo = Build.MODEL ?: "0",
|
||||
deviceVersion = Build.VERSION.RELEASE,
|
||||
language = ""
|
||||
language = Localization.getDeviceLanguage()
|
||||
)
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,78 @@
|
||||
package com.taskttl.core.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.net.toUri
|
||||
import com.taskttl.MainApplication
|
||||
|
||||
actual object ExternalAppLauncher {
|
||||
private val appContext: Context
|
||||
get() = MainApplication.instance.applicationContext
|
||||
|
||||
private val packageName = appContext.packageName
|
||||
|
||||
/**
|
||||
* 打开应用程序评级
|
||||
*/
|
||||
actual suspend fun openAppRating() {
|
||||
val webUrl = "https://play.google.com/store/apps/details?id=$packageName"
|
||||
try {
|
||||
// 尝试打开 Google Play 应用
|
||||
val marketUri = "market://details?id=$packageName"
|
||||
if (openUriWithFallback(marketUri, webUrl)) {
|
||||
return
|
||||
}
|
||||
|
||||
// 如果前面的方法都失败,直接打开网页版
|
||||
openWebUrl(webUrl)
|
||||
} catch (e: Exception) {
|
||||
// Log.e(TAG, getString(Res.string.external_app_launcher_open_rating_failed), e)
|
||||
openWebUrl(webUrl)
|
||||
}
|
||||
}
|
||||
|
||||
actual suspend fun openUrl(url: String) {
|
||||
openWebUrl(url)
|
||||
}
|
||||
|
||||
/**
|
||||
* 尝试打开URI,打开指定的应用包名
|
||||
* @return 是否成功打开
|
||||
*/
|
||||
private fun openUriWithFallback(uri: String, packageName: String): Boolean {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri.toUri())
|
||||
intent.setPackage(packageName)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
// 检查是否有应用可以处理此Intent
|
||||
if (isIntentResolvable(intent)) {
|
||||
appContext.startActivity(intent)
|
||||
return true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
// Log.w(TAG, getString(Res.string.external_app_launcher_open_uri_failed, uri), e)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开网页URL
|
||||
*/
|
||||
private fun openWebUrl(url: String) {
|
||||
try {
|
||||
val intent = Intent(Intent.ACTION_VIEW, url.toUri())
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
appContext.startActivity(intent)
|
||||
} catch (e: Exception) {
|
||||
// Log.e(TAG, getString(Res.string.external_app_launcher_open_web_failed, url), e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查Intent是否可以被解析
|
||||
*/
|
||||
private fun isIntentResolvable(intent: Intent): Boolean {
|
||||
return intent.resolveActivity(appContext.packageManager) != null
|
||||
}
|
||||
}
|
@@ -0,0 +1,32 @@
|
||||
package com.taskttl.core.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.os.LocaleList
|
||||
import com.taskttl.MainApplication
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 本地化
|
||||
* @author DevTTL
|
||||
* @date 2025/10/14
|
||||
*/
|
||||
actual object Localization {
|
||||
private val appContext: Context
|
||||
get() = MainApplication.instance.applicationContext
|
||||
|
||||
actual fun applyLanguage(iso: String) {
|
||||
val locale = Locale.forLanguageTag(iso)
|
||||
Locale.setDefault(locale)
|
||||
val config = appContext.resources.configuration
|
||||
// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
config.setLocales(LocaleList(locale))
|
||||
// } else {
|
||||
// config.setLocale(locale)
|
||||
// }
|
||||
appContext.createConfigurationContext(config)
|
||||
}
|
||||
|
||||
actual fun getDeviceLanguage(): String {
|
||||
return Locale.getDefault().language
|
||||
}
|
||||
}
|
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
Before Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 564 B |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 825 B |
Before Width: | Height: | Size: 4.8 KiB After Width: | Height: | Size: 871 B |
Before Width: | Height: | Size: 7.3 KiB After Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 7.7 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -1,313 +1,317 @@
|
||||
<resources>
|
||||
<!-- Onboarding -->
|
||||
<string name="continue_text">继续</string>
|
||||
<string name="skip_text">跳过</string>
|
||||
<string name="get_start_text">开始使用</string>
|
||||
<string name="continue_text">Continue</string>
|
||||
<string name="skip_text">Skip</string>
|
||||
<string name="get_start_text">Get Started</string>
|
||||
|
||||
<string name="onboarding_welcome_title">欢迎使用 TaskTTL</string>
|
||||
<string name="onboarding_welcome_desc">一个简洁而强大的任务管理工具,帮助您高效管理日常任务和重要日期</string>
|
||||
<string name="onboarding_smart_title">智能任务管理</string>
|
||||
<string name="onboarding_smart_desc">创建、分类和跟踪您的任务。设置优先级,添加截止日期,让工作更有条理</string>
|
||||
<string name="onboarding_dates_title">重要日期提醒</string>
|
||||
<string name="onboarding_dates_desc">设置重要日期的倒数计时,永远不会错过生日、纪念日或重要的截止日期</string>
|
||||
<string name="onboarding_ready_title">准备就绪!</string>
|
||||
<string name="onboarding_ready_desc">现在您可以开始创建第一个任务,让我们一起提高工作效率吧!</string>
|
||||
<string name="onboarding_welcome_title">Welcome to TaskTTL</string>
|
||||
<string name="onboarding_welcome_desc">A simple yet powerful task management tool that helps you efficiently manage your daily tasks and important dates</string>
|
||||
<string name="onboarding_smart_title">Smart Task Management</string>
|
||||
<string name="onboarding_smart_desc">Create, categorize, and track your tasks. Set priorities and add due dates for better organization</string>
|
||||
<string name="onboarding_dates_title">Important Date Reminders</string>
|
||||
<string name="onboarding_dates_desc">Set countdowns to important dates and never miss a birthday, anniversary, or important deadline</string>
|
||||
<string name="onboarding_ready_title">Ready! </string>
|
||||
<string name="onboarding_ready_desc">Now you can start creating your first task. Let's improve your productivity together! </string>
|
||||
|
||||
<!-- 应用信息 -->
|
||||
<!-- App Information -->
|
||||
<string name="app_name">TaskTTL</string>
|
||||
<string name="app_name_description">任务管理与倒数日应用</string>
|
||||
<string name="app_name_remark">让每一天都更有意义</string>
|
||||
<string name="version">版本</string>
|
||||
<string name="build_version">构建版本</string>
|
||||
<string name="app_name_description">Task Management and Countdown App</string>
|
||||
<string name="app_name_remark">Make Every Day More Meaningful</string>
|
||||
<string name="version">Version</string>
|
||||
<string name="build_version">Build Version</string>
|
||||
|
||||
<!-- 导航栏 -->
|
||||
<string name="nav_todo">待办</string>
|
||||
<string name="nav_countdown">倒数日</string>
|
||||
<string name="nav_statistics">统计</string>
|
||||
<string name="nav_settings">设置</string>
|
||||
<!-- Navigation Bar -->
|
||||
<string name="nav_todo">To Do</string>
|
||||
<string name="nav_countdown">Countdown</string>
|
||||
<string name="nav_statistics">Statistics</string>
|
||||
<string name="nav_settings">Settings</string>
|
||||
|
||||
<!-- 任务模块 -->
|
||||
<string name="search_placeholder">搜索任务...</string>
|
||||
<string name="title_task">我的任务</string>
|
||||
<string name="title_task_info">任务详情</string>
|
||||
<string name="title_add_task">添加任务</string>
|
||||
<string name="title_edit_task">编辑任务</string>
|
||||
<!-- Task Module -->
|
||||
<string name="search_placeholder">Search Tasks...</string>
|
||||
<string name="title_task">My Tasks</string>
|
||||
<string name="title_task_info">Task Details</string>
|
||||
<string name="title_add_task">Add Task</string>
|
||||
|
||||
<string name="label_task_list">任务列表</string>
|
||||
<string name="label_show_completed">显示已完成</string>
|
||||
<string name="text_no_tasks">暂无任务</string>
|
||||
<string name="text_add_task_hint">点击右下角按钮添加新任务</string>
|
||||
<string name="desc_completed">已完成</string>
|
||||
<string name="desc_incomplete">未完成</string>
|
||||
<string name="title_edit_task">Edit Task</string>
|
||||
|
||||
<string name="title_task_title">任务标题</string>
|
||||
<string name="title_task_description">任务描述</string>
|
||||
<string name="title_select_category">选择分类</string>
|
||||
<string name="title_priority">优先级</string>
|
||||
<string name="title_due_date">截止日期(可选)</string>
|
||||
<string name="desc_select_date">选择日期</string>
|
||||
<string name="title_tags">标签(用逗号分隔)</string>
|
||||
<string name="hint_tags">例如:重要,紧急,工作</string>
|
||||
<string name="label_task_list">Task List</string>
|
||||
<string name="label_show_completed">Show Completed</string>
|
||||
<string name="text_no_tasks">No Tasks</string>
|
||||
<string name="text_add_task_hint">Click the button in the lower right corner to add a new task</string>
|
||||
<string name="desc_completed">Completed</string>
|
||||
<string name="desc_incomplete">Incomplete</string>
|
||||
|
||||
<string name="text_task_not_found">任务不存在</string>
|
||||
<string name="label_due_date">截止日期:</string>
|
||||
<string name="label_none">无</string>
|
||||
<string name="label_created_at">创建时间:</string>
|
||||
<string name="label_description">任务描述</string>
|
||||
<string name="title_task_title">Task Title</string>
|
||||
<string name="title_task_description">Task Description</string>
|
||||
<string name="title_select_category">Select Category</string>
|
||||
<string name="title_priority">Priority</string>
|
||||
<string name="title_due_date">Due Date (Optional)</string>
|
||||
<string name="desc_select_date">Select a date</string>
|
||||
<string name="title_tags">Tags (comma-separated)</string>
|
||||
<string name="hint_tags">Example: Important, Urgent, Work</string>
|
||||
|
||||
<string name="task_add_success">任务添加成功</string>
|
||||
<string name="task_add_failed">添加任务失败</string>
|
||||
<string name="task_update_success">任务更新成功</string>
|
||||
<string name="task_update_failed">更新任务失败</string>
|
||||
<string name="task_delete_success">任务删除成功</string>
|
||||
<string name="task_delete_failed">删除任务失败</string>
|
||||
<string name="task_load_failed">加载任务失败</string>
|
||||
<string name="task_query_failed">查询任务失败</string>
|
||||
<string name="task_status_update_success">更新任务状态成功</string>
|
||||
<string name="task_status_update_failed">更新任务状态失败</string>
|
||||
<string name="text_task_not_found">Task does not exist</string>
|
||||
<string name="label_due_date">Due date:</string>
|
||||
<string name="label_none">None</string>
|
||||
<string name="label_created_at">Creation time:</string>
|
||||
<string name="label_description">Task description</string>
|
||||
|
||||
<!-- 倒数日模块 -->
|
||||
<string name="title_countdown">倒数日</string>
|
||||
<string name="title_countdown_info">倒数日详情</string>
|
||||
<string name="title_add_countdown">添加倒数日</string>
|
||||
<string name="title_edit_countdown">编辑倒数日</string>
|
||||
<string name="task_add_success">Task added successfully</string>
|
||||
<string name="task_add_failed">Task addition failed</string>
|
||||
<string name="task_update_success">Task updated successfully</string>
|
||||
<string name="task_update_failed">Task update failed</string>
|
||||
<string name="task_delete_success">Task deleted successfully</string>
|
||||
<string name="task_delete_failed">Task deletion failed</string>
|
||||
<string name="task_load_failed">Task loading failed</string>
|
||||
<string name="task_query_failed">Task query failed</string>
|
||||
<string name="task_status_update_success">Task status update successful</string>
|
||||
<string name="task_status_update_failed">Task status update failed</string>
|
||||
|
||||
<string name="label_countdown_list">倒数日列表</string>
|
||||
<string name="label_days">天</string>
|
||||
<string name="text_no_countdowns">暂无倒数日</string>
|
||||
<string name="text_add_countdown_tip">点击右下角按钮添加新的倒数日</string>
|
||||
<string name="desc_add_countdown">添加倒数日</string>
|
||||
<!-- Countdown Module -->
|
||||
<string name="title_countdown">Countdown</string>
|
||||
<string name="title_countdown_info">Countdown Details</string>
|
||||
<string name="title_add_countdown">Add Countdown</string>
|
||||
<string name="title_edit_countdown">Edit Countdown</string>
|
||||
|
||||
<string name="label_countdown_title">倒数日标题</string>
|
||||
<string name="label_countdown_description">倒数日描述</string>
|
||||
<string name="label_target_date">目标日期</string>
|
||||
<string name="label_notification_setting">通知设置</string>
|
||||
<string name="countdown_not_found">倒数日不存在</string>
|
||||
<string name="event_description">事件描述</string>
|
||||
<string name="detail_information">详细信息</string>
|
||||
<string name="reminder">提醒</string>
|
||||
<string name="label_countdown_list">Countdown List</string>
|
||||
<string name="label_days">Days</string>
|
||||
<string name="text_no_countdowns">No Countdown</string>
|
||||
<string name="text_add_countdown_tip">Click the button in the lower right corner to add a new countdown</string>
|
||||
<string name="desc_add_countdown">Add Countdown</string>
|
||||
|
||||
<string name="countdown_add_success">倒数日添加成功</string>
|
||||
<string name="countdown_add_failed">添加倒数日失败</string>
|
||||
<string name="countdown_update_success">倒数日更新成功</string>
|
||||
<string name="countdown_update_failed">更新倒数日失败</string>
|
||||
<string name="countdown_delete_success">倒数日删除成功</string>
|
||||
<string name="countdown_delete_failed">删除倒数日失败</string>
|
||||
<string name="countdown_load_failed">加载倒数日失败</string>
|
||||
<string name="countdown_query_failed">查询倒数日失败</string>
|
||||
<string name="label_countdown_title">Countdown Title</string>
|
||||
<string name="label_countdown_description">Countdown Description</string>
|
||||
<string name="label_target_date">Target Date</string>
|
||||
<string name="label_notification_setting">Notification settings</string>
|
||||
<string name="countdown_not_found">Countdown day does not exist</string>
|
||||
<string name="event_description">Event description</string>
|
||||
<string name="detail_information">Detailed information</string>
|
||||
<string name="reminder">Reminder</string>
|
||||
|
||||
<!-- 统计模块 -->
|
||||
<string name="title_statistics">统计</string>
|
||||
<string name="overview">总览</string>
|
||||
<string name="category_statistics">分类统计</string>
|
||||
<string name="total_tasks">总任务</string>
|
||||
<string name="completed">已完成</string>
|
||||
<string name="completion_rate">完成率</string>
|
||||
<string name="total_countdowns">倒数日总数</string>
|
||||
<string name="active">活跃中</string>
|
||||
<string name="countdown_add_success">Countdown day added successfully</string>
|
||||
<string name="countdown_add_failed">Countdown day added failed</string>
|
||||
<string name="countdown_update_success">Countdown day updated successfully</string>
|
||||
<string name="countdown_update_failed">Countdown day updated failed</string>
|
||||
<string name="countdown_delete_success">Countdown day deleted successfully</string>
|
||||
<string name="countdown_delete_failed">Countdown day deleted failed</string>
|
||||
<string name="countdown_load_failed">Countdown day loaded failed</string>
|
||||
<string name="countdown_query_failed">Countdown query failed</string>
|
||||
|
||||
<!-- 分类模块 -->
|
||||
<string name="category_task">任务</string>
|
||||
<string name="category_countdown">倒数日</string>
|
||||
<!-- Statistics Module -->
|
||||
<string name="title_statistics">Statistics</string>
|
||||
<string name="overview">Overview</string>
|
||||
<string name="category_statistics">Category statistics</string>
|
||||
<string name="total_tasks">Total tasks</string>
|
||||
<string name="completed">Completed</string>
|
||||
<string name="completion_rate">Completion rate</string>
|
||||
<string name="total_countdowns">Total countdown days</string>
|
||||
<string name="active">Active</string>
|
||||
|
||||
<string name="title_category">分类管理</string>
|
||||
<string name="title_add_category">添加分类</string>
|
||||
<string name="title_edit_category">编辑分类</string>
|
||||
<string name="label_no_category">暂无分类</string>
|
||||
<string name="label_add_category_hint">点击右下角按钮添加新分类</string>
|
||||
<string name="label_category_name">分类名称</string>
|
||||
<string name="placeholder_category_name">输入分类名称...</string>
|
||||
<string name="label_category_type">分类类型</string>
|
||||
<string name="label_select_color">选择颜色</string>
|
||||
<string name="label_select_icon">选择图标</string>
|
||||
<string name="label_task_category">任务分类</string>
|
||||
<string name="label_countdown_category">倒数日分类</string>
|
||||
<!-- Category Module -->
|
||||
<string name="category_task">Task</string>
|
||||
<string name="category_countdown">Countdown</string>
|
||||
|
||||
<string name="label_task_count">%1$d 个任务</string>
|
||||
<string name="label_countdown_count">%1$d 个倒数日</string>
|
||||
<string name="title_category">Category Management</string>
|
||||
<string name="title_add_category">Add Category</string>
|
||||
<string name="title_edit_category">Edit Category</string>
|
||||
<string name="label_no_category">No Category</string>
|
||||
<string name="label_add_category_hint">Click the button in the lower right corner to add a new category</string>
|
||||
<string name="label_category_name">Category Name</string>
|
||||
<string name="placeholder_category_name">Enter Category Name...</string>
|
||||
<string name="label_category_type">Category Type</string>
|
||||
<string name="label_select_color">Select Color</string>
|
||||
<string name="label_select_icon">Select Icon</string>
|
||||
<string name="label_task_category">Task Category</string>
|
||||
<string name="label_countdown_category">Countdown Category</string>
|
||||
|
||||
<!-- 分类操作反馈 -->
|
||||
<string name="category_add_success">分类添加成功</string>
|
||||
<string name="category_add_failed">添加分类失败</string>
|
||||
<string name="category_update_success">分类更新成功</string>
|
||||
<string name="category_update_failed">更新分类失败</string>
|
||||
<string name="category_delete_success">分类删除成功</string>
|
||||
<string name="category_delete_failed">删除分类失败</string>
|
||||
<string name="category_load_failed">加载分类失败</string>
|
||||
<string name="category_stat_failed">加载统计数据失败</string>
|
||||
<string name="category_init_success">默认分类初始化成功</string>
|
||||
<string name="category_init_failed">初始化默认分类失败</string>
|
||||
<string name="category_not_found">查询分类失败</string>
|
||||
<string name="category_count_update_failed">更新分类计数失败</string>
|
||||
<string name="label_task_count">%1$d tasks</string>
|
||||
<string name="label_countdown_count">%1$d countdown days</string>
|
||||
|
||||
<!-- 通用操作 -->
|
||||
<string name="enter">进入</string>
|
||||
<string name="edit">编辑</string>
|
||||
<string name="cancel">取消</string>
|
||||
<string name="confirm">确定</string>
|
||||
<string name="delete">删除</string>
|
||||
<string name="export">导出</string>
|
||||
<string name="import">导入</string>
|
||||
<string name="back">返回</string>
|
||||
<string name="action">操作</string>
|
||||
<string name="search">搜索</string>
|
||||
<string name="clear_text">清除</string>
|
||||
<string name="all_text">全部</string>
|
||||
<string name="retry">重试</string>
|
||||
<string name="choose_file">选择文件</string>
|
||||
<string name="error_title">错误</string>
|
||||
<string name="webview_loading_error">加载失败,请检查网络连接</string>
|
||||
<!-- Category Operation Feedback -->
|
||||
<string name="category_add_success">Category added successfully</string>
|
||||
<string name="category_add_failed">Category added failed</string>
|
||||
<string name="category_update_success">Category updated successfully</string>
|
||||
<string name="category_update_failed">Category updated failed</string>
|
||||
<string name="category_delete_success">Category deleted successfully</string>
|
||||
<string name="category_delete_failed">Category deleted failed</string>
|
||||
<string name="category_load_failed">Category loaded failed</string>
|
||||
<string name="category_stat_failed">Statistics loaded failed</string>
|
||||
<string name="category_init_success">Default category initialized successfully</string>
|
||||
<string name="category_init_failed">Default category initialized failed</string>
|
||||
<string name="category_not_found">Category query failed</string>
|
||||
<string name="category_count_update_failed">Category count update failed</string>
|
||||
|
||||
<!-- Common Operations -->
|
||||
<string name="enter">Enter</string>
|
||||
<string name="edit">Edit</string>
|
||||
<string name="cancel">Cancel</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
<string name="delete">Delete</string>
|
||||
<string name="export">Export</string>
|
||||
<string name="import">Import</string>
|
||||
<string name="back">Back</string>
|
||||
<string name="action">Action</string>
|
||||
<string name="search">Search</string>
|
||||
<string name="clear_text">Clear</string>
|
||||
<string name="all_text">All</string>
|
||||
<string name="retry">Retry</string>
|
||||
<string name="choose_file">Choose File</string>
|
||||
<string name="error_title">Error</string>
|
||||
<string name="loading">Loading...</string>
|
||||
<string name="webview_loading_error">Loading failed, please check your network connection</string>
|
||||
|
||||
|
||||
<!-- 数据管理 -->
|
||||
<string name="title_data_management">数据管理</string>
|
||||
<string name="title_export_data">导出数据</string>
|
||||
<string name="desc_export_data">将所有任务和倒数日导出为文件</string>
|
||||
<string name="title_import_data">导入数据</string>
|
||||
<string name="desc_import_data">从文件导入任务和倒数日</string>
|
||||
<string name="title_auto_backup">自动备份</string>
|
||||
<string name="desc_auto_backup">定期自动备份数据到云端</string>
|
||||
<string name="title_clear_all_data">清除所有数据</string>
|
||||
<string name="desc_clear_all_data">删除所有任务、倒数日和设置</string>
|
||||
<string name="desc_clear_all_data_dialog">此操作将删除所有任务、倒数日和设置,且无法恢复。</string>
|
||||
<string name="title_clear_completed_tasks">清理已完成任务</string>
|
||||
<string name="title_clear_expired_countdowns">清理过期倒数日</string>
|
||||
<string name="desc_clear_completed_tasks">删除所有已完成的任务</string>
|
||||
<string name="desc_clear_expired_countdowns">删除所有已过期的倒数日</string>
|
||||
<!-- Data Management -->
|
||||
<string name="title_data_management">Data Management</string>
|
||||
<string name="title_export_data">Export Data</string>
|
||||
<string name="desc_export_data">Export all tasks and countdowns to a file</string>
|
||||
<string name="title_import_data">Import Data</string>
|
||||
<string name="desc_import_data">Import tasks and countdowns from a file</string>
|
||||
<string name="title_auto_backup">Auto Backup</string>
|
||||
<string name="desc_auto_backup">Regularly and automatically back up data to the cloud</string>
|
||||
<string name="title_clear_all_data">Clear All Data</string>
|
||||
<string name="desc_clear_all_data">Delete all tasks, countdowns, and settings</string>
|
||||
<string name="desc_clear_all_data_dialog">This operation will delete all tasks, countdowns, and settings and cannot be restored. </string>
|
||||
<string name="title_clear_completed_tasks">Clear completed tasks</string>
|
||||
<string name="title_clear_expired_countdowns">Clear expired countdowns</string>
|
||||
<string name="desc_clear_completed_tasks">Delete all completed tasks</string>
|
||||
<string name="desc_clear_expired_countdowns">Delete all expired countdowns</string>
|
||||
|
||||
<string name="title_backup_restore">备份与恢复</string>
|
||||
<string name="title_data_clean">数据清理</string>
|
||||
<string name="label_select_import_file">选择要导入的文件</string>
|
||||
<string name="label_select_file">选择文件</string>
|
||||
<string name="label_select_export_format">选择导出格式</string>
|
||||
<string name="label_json_format">JSON格式</string>
|
||||
<string name="label_csv_format">CSV格式</string>
|
||||
<string name="title_backup_restore">Backup and restore</string>
|
||||
<string name="title_data_clean">Data cleanup</string>
|
||||
<string name="label_select_import_file">Select the file to import</string>
|
||||
<string name="label_select_file">Select a file</string>
|
||||
<string name="label_select_export_format">Select the export format</string>
|
||||
<string name="label_json_format">JSON format</string>
|
||||
<string name="label_csv_format">CSV format</string>
|
||||
|
||||
<!-- 设置模块 -->
|
||||
<string name="title_app_settings">应用设置</string>
|
||||
<string name="section_general_settings">通用设置</string>
|
||||
<string name="section_data_management">数据管理</string>
|
||||
<string name="section_social_share">社交分享</string>
|
||||
<string name="section_help_feedback">帮助与反馈</string>
|
||||
<!-- Settings Module -->
|
||||
<string name="title_app_settings">App Settings</string>
|
||||
<string name="section_general_settings">General Settings</string>
|
||||
<string name="section_data_management">Data Management</string>
|
||||
<string name="section_social_share">Social Sharing</string>
|
||||
<string name="section_help_feedback">Help and Feedback</string>
|
||||
|
||||
<string name="setting_push_notification">推送通知</string>
|
||||
<string name="setting_push_notification_desc">接收任务和倒数日提醒</string>
|
||||
<string name="setting_dark_mode">深色模式</string>
|
||||
<string name="setting_dark_mode_desc">使用深色主题</string>
|
||||
<string name="setting_language">语言设置</string>
|
||||
<string name="setting_language_desc">简体中文</string>
|
||||
<string name="setting_push_notification">Push Notifications</string>
|
||||
<string name="setting_push_notification_desc">Receive Task and Countdown Reminders</string>
|
||||
<string name="setting_dark_mode">Dark Mode</string>
|
||||
<string name="setting_dark_mode_desc">Use Dark Theme</string>
|
||||
<string name="setting_language">Language Settings</string>
|
||||
<string name="setting_language_desc">Simplified Chinese</string>
|
||||
|
||||
<string name="setting_category_management">分类管理</string>
|
||||
<string name="setting_category_management_desc">管理分类</string>
|
||||
<string name="setting_data_management">数据管理</string>
|
||||
<string name="setting_data_management_desc">备份和恢复数据</string>
|
||||
<string name="setting_category_management">Management</string>
|
||||
<string name="setting_category_management_desc">Manage categories</string>
|
||||
<string name="setting_data_management">Data management</string>
|
||||
<string name="setting_data_management_desc">Backup and restore data</string>
|
||||
|
||||
<string name="setting_share_achievement">分享成就</string>
|
||||
<string name="setting_share_achievement_desc">分享任务完成成就</string>
|
||||
<string name="setting_invite_friend">推荐给朋友</string>
|
||||
<string name="setting_invite_friend_desc">邀请朋友使用 TaskMaster</string>
|
||||
<string name="setting_share_achievement">Share achievements</string>
|
||||
<string name="setting_share_achievement_desc">Share task completion achievements</string>
|
||||
<string name="setting_invite_friend">Recommend to a friend</string>
|
||||
<string name="setting_invite_friend_desc">Invite a friend to use TaskMaster</string>
|
||||
|
||||
<string name="setting_feedback">意见反馈</string>
|
||||
<string name="setting_feedback_desc">告诉我们您的想法</string>
|
||||
<string name="setting_privacy_policy">隐私政策</string>
|
||||
<string name="setting_privacy_policy_desc">了解我们如何保护您的数据</string>
|
||||
<string name="setting_about_app">关于应用</string>
|
||||
<string name="setting_about_app_desc">版本 1.0.0</string>
|
||||
<string name="setting_feedback">Feedback</string>
|
||||
<string name="setting_feedback_desc">Tell us what you think</string>
|
||||
<string name="setting_privacy_policy">Privacy Policy</string>
|
||||
<string name="setting_privacy_policy_desc">Learn how we protect your data</string>
|
||||
<string name="setting_privacy_rate">App Review</string>
|
||||
<string name="setting_privacy_rate_desc">If you like it, please leave a five-star review in the store</string>
|
||||
<string name="setting_about_app">About the App</string>
|
||||
<string name="app_version_code">100001</string>
|
||||
<string name="app_version_name">1.0.1</string>
|
||||
<string name="setting_about_app_desc">Version 1.0.1</string>
|
||||
|
||||
<!-- 反馈与帮助 -->
|
||||
<string name="title_feedback">意见反馈</string>
|
||||
<string name="feedback_type">反馈类型</string>
|
||||
<string name="feedback_issue">问题反馈</string>
|
||||
<string name="feedback_suggestion">功能建议</string>
|
||||
<string name="feedback_description">问题描述</string>
|
||||
<string name="feedback_placeholder">请详细描述您遇到的问题或建议...</string>
|
||||
<string name="feedback_contact">联系方式(可选)</string>
|
||||
<string name="feedback_contact_placeholder">您的邮箱地址,方便我们回复</string>
|
||||
<string name="feedback_submitted">感谢您的反馈!我们会尽快处理。</string>
|
||||
<string name="feedback_error_empty">请填写反馈内容</string>
|
||||
<string name="button_send_feedback">发送反馈</string>
|
||||
<!-- Feedback & Help -->
|
||||
<string name="title_feedback">Feedback</string>
|
||||
<string name="feedback_type">Feedback Type</string>
|
||||
<string name="feedback_issue">Issue Feedback</string>
|
||||
<string name="feedback_suggestion">Feature Suggestion</string>
|
||||
<string name="feedback_description">Issue Description</string>
|
||||
<string name="feedback_placeholder">Please describe your problem or suggestion in detail...</string>
|
||||
<string name="feedback_contact">Contact Information (optional)</string>
|
||||
<string name="feedback_contact_placeholder">Your email address for our response</string>
|
||||
<string name="feedback_submitted">Thank you for your feedback! We will address it as soon as possible.</string>
|
||||
<string name="feedback_error_empty">Please fill in your feedback</string>
|
||||
<string name="button_send_feedback">Send Feedback</string>
|
||||
|
||||
<!-- 关于页面 -->
|
||||
<string name="title_about">关于</string>
|
||||
<string name="app_intro_title">应用介绍</string>
|
||||
<string name="app_intro_content">
|
||||
TaskTTL 是一款现代化的任务管理与倒数日应用,
|
||||
支持分类管理、优先级设置与统计分析,让生活更有条理。
|
||||
</string>
|
||||
<!-- About Page -->
|
||||
<string name="title_about">About</string>
|
||||
<string name="app_intro_title">App Introduction</string>
|
||||
<string name="app_intro_content">TaskTTL is a modern task management and countdown app.\nIt supports category management, priority setting, and statistical analysis, making your life more organized. </string>
|
||||
|
||||
<string name="title_privacy">隐私协议</string>
|
||||
<string name="title_privacy">Privacy Policy</string>
|
||||
|
||||
<string name="tech_stack">技术栈</string>
|
||||
<string name="tech_stack_kmp">Kotlin Multiplatform(跨平台开发框架)</string>
|
||||
<string name="tech_stack_compose">Jetpack Compose(现代化 UI 框架)</string>
|
||||
<string name="tech_stack_room">Room Database(本地存储)</string>
|
||||
<string name="tech_stack_koin">Koin(依赖注入框架)</string>
|
||||
<string name="tech_stack_ktor">Ktor(网络请求)</string>
|
||||
<string name="tech_stack_mvi">MVI Architecture(响应式架构模式)</string>
|
||||
<string name="tech_stack">Technology Stack</string>
|
||||
<string name="tech_stack_kmp">Kotlin Multiplatform (Cross-Platform Development Framework)</string>
|
||||
<string name="tech_stack_compose">Jetpack Compose (Modern UI Framework)</string>
|
||||
<string name="tech_stack_room">Room Database (Local Storage)</string>
|
||||
<string name="tech_stack_koin">Koin (Dependency Injection Framework)</string>
|
||||
<string name="tech_stack_ktor">Ktor (Network Request)</string>
|
||||
<string name="tech_stack_mvi">MVI Architecture (Responsive Architecture Pattern)</string>
|
||||
|
||||
<string name="developer_text">开发者</string>
|
||||
<string name="devttl_team">DevTTL 团队</string>
|
||||
<string name="contact_us">联系我们</string>
|
||||
<string name="email_text">电子邮箱</string>
|
||||
<string name="developer_text">Developer</string>
|
||||
<string name="devttl_team">DevTTL Team</string>
|
||||
<string name="contact_us">Contact Us</string>
|
||||
<string name="email_text">Email</string>
|
||||
<string name="setting_privacy_email_uri">mailto:%1$s</string>
|
||||
<string name="email">admin@devttl.com</string>
|
||||
<string name="web_text">官方网站</string>
|
||||
<string name="web_text">Official Website</string>
|
||||
<string name="web_url">https://devttl.com</string>
|
||||
<string name="copyright_year">2025</string>
|
||||
<string name="all_rights_reserved">保留所有权利</string>
|
||||
<string name="all_rights_reserved">All Rights Reserved</string>
|
||||
|
||||
<string name="priority_low">低</string>
|
||||
<string name="priority_medium">中</string>
|
||||
<string name="priority_high">高</string>
|
||||
<string name="priority_urgent">紧急</string>
|
||||
<string name="priority_low">Low</string>
|
||||
<string name="priority_medium">Medium</string>
|
||||
<string name="priority_high">High</string>
|
||||
<string name="priority_urgent">Urgent</string>
|
||||
|
||||
<string name="reminder_once">一次</string>
|
||||
<string name="reminder_daily">每天</string>
|
||||
<string name="reminder_weekly">每周</string>
|
||||
<string name="reminder_monthly">每月</string>
|
||||
<string name="reminder_off">关闭</string>
|
||||
<string name="reminder_once">Once</string>
|
||||
<string name="reminder_daily">Daily</string>
|
||||
<string name="reminder_weekly">Weekly</string>
|
||||
<string name="reminder_monthly">Monthly</string>
|
||||
<string name="reminder_off">Off</string>
|
||||
|
||||
<!-- 工作与学习 -->
|
||||
<string name="category_briefcase">工作</string>
|
||||
<string name="category_book">学习</string>
|
||||
<string name="category_exam">考试</string>
|
||||
<string name="category_project">项目</string>
|
||||
<!-- Work & Study -->
|
||||
<string name="category_briefcase">Work</string>
|
||||
<string name="category_book">Study</string>
|
||||
<string name="category_exam">Exam</string>
|
||||
<string name="category_project">Project</string>
|
||||
|
||||
<!-- 生活与家庭 -->
|
||||
<string name="category_home">家庭</string>
|
||||
<string name="category_coffee">休闲</string>
|
||||
<string name="category_shopping">购物</string>
|
||||
<string name="category_food">美食</string>
|
||||
<string name="category_cleaning">家务</string>
|
||||
<!-- Life & Family -->
|
||||
<string name="category_home">Family</string>
|
||||
<string name="category_coffee">Leisure</string>
|
||||
<string name="category_shopping">Shopping</string>
|
||||
<string name="category_food">Food</string>
|
||||
<string name="category_cleaning">Housework</string>
|
||||
|
||||
<!-- 健康与运动 -->
|
||||
<string name="category_heart">健康</string>
|
||||
<string name="category_dumbbell">健身</string>
|
||||
<string name="category_sleep">作息</string>
|
||||
<!-- Health & Fitness -->
|
||||
<string name="category_heart">Health</string>
|
||||
<string name="category_dumbbell">Fitness</string>
|
||||
<string name="category_sleep">Days and Nights</string>
|
||||
|
||||
<!-- 娱乐与兴趣 -->
|
||||
<string name="category_music">音乐</string>
|
||||
<string name="category_gamepad">娱乐</string>
|
||||
<string name="category_camera">摄影</string>
|
||||
<string name="category_movie">影视</string>
|
||||
<!-- Entertainment & Hobbies -->
|
||||
<string name="category_music">Music</string>
|
||||
<string name="category_gamepad">Entertainment</string>
|
||||
<string name="category_camera">Photography</string>
|
||||
<string name="category_movie">Movies</string>
|
||||
|
||||
<!-- 出行与旅行 -->
|
||||
<string name="category_car">出行</string>
|
||||
<string name="category_plane">旅行</string>
|
||||
<string name="category_walk">步行</string>
|
||||
<!-- Travel & Tours -->
|
||||
<string name="category_car">Travel</string>
|
||||
<string name="category_plane">Travel</string>
|
||||
<string name="category_walk">Walk</string>
|
||||
|
||||
<!-- 节日与纪念 -->
|
||||
<string name="category_birthday">生日</string>
|
||||
<string name="category_festival">节日</string>
|
||||
<string name="category_anniversary">纪念日</string>
|
||||
<!-- Holidays & Anniversaries -->
|
||||
<string name="category_birthday">Birthday</string>
|
||||
<string name="category_festival">Festival</string>
|
||||
<string name="category_anniversary">Anniversary</string>
|
||||
|
||||
<!-- 财务与计划 -->
|
||||
<string name="category_money">理财</string>
|
||||
<string name="category_goal">目标</string>
|
||||
<string name="category_reminder">提醒</string>
|
||||
<!-- Finance & Planning -->
|
||||
<string name="category_money">Financial Planning</string>
|
||||
<string name="category_goal">Goal</string>
|
||||
<string name="category_reminder">Reminder</string>
|
||||
|
||||
<string name="privacy_url">https://sites.google.com/view/taskttl/privacy</string>
|
||||
|
||||
<string name="feedback_success">反馈成功</string>
|
||||
<string name="feedback_error">反馈失败,请检查网络连接或稍后重试</string>
|
||||
<string name="feedback_success">Feedback successful</string>
|
||||
<string name="feedback_error">Feedback failed. Please check your network connection or try again later.</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -41,8 +41,5 @@ enum class PointEvent(val eventName: String, val eventCode: Int) {
|
||||
AdShownBanner("ad_shown_banner", 9001),
|
||||
AdClickReward("ad_click_reward", 9002);
|
||||
|
||||
fun toMap(): Map<String, Any> = mapOf(
|
||||
"eventName" to eventName,
|
||||
"eventCode" to eventCode,
|
||||
)
|
||||
fun toMap(): Map<String, Any> = mapOf("eventName" to eventName, "eventCode" to eventCode)
|
||||
}
|
@@ -7,8 +7,8 @@ package com.taskttl.core.network
|
||||
*/
|
||||
object ApiConfig {
|
||||
/** 基本地址 */
|
||||
const val BASE_URL = "http://10.0.0.5:8888/api/v1"
|
||||
// const val BASE_URL = "https://api.tikttl.com/api/v1"
|
||||
// const val BASE_URL = "http://10.0.0.5:8888/api/v1"
|
||||
const val BASE_URL = "https://api.taskttl.com/api/v1"
|
||||
|
||||
/** 反馈地址 */
|
||||
const val FEEDBACK_URL = "$BASE_URL/feedback"
|
||||
|
@@ -0,0 +1,131 @@
|
||||
package com.taskttl.core.ui
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.animateFloat
|
||||
import androidx.compose.animation.core.infiniteRepeatable
|
||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.blur
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlinx.coroutines.delay
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
import taskttl.composeapp.generated.resources.Res
|
||||
import taskttl.composeapp.generated.resources.loading
|
||||
|
||||
@Composable
|
||||
fun LoadingOverlay(
|
||||
visible: Boolean,
|
||||
messageRes: StringResource = Res.string.loading,
|
||||
) {
|
||||
AnimatedVisibility(
|
||||
visible = visible,
|
||||
enter = fadeIn(animationSpec = tween(300)),
|
||||
exit = fadeOut(animationSpec = tween(300))
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color.Black.copy(alpha = 0.4f))
|
||||
.blur(8.dp)
|
||||
// 拦截所有点击事件,防止点击到底层
|
||||
.clickable(
|
||||
indication = null, // 无点击动画
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
RotatingGradientRing(
|
||||
size = 56.dp,
|
||||
stroke = 6.dp,
|
||||
gradientColors = listOf(
|
||||
Color(0xFF4285F4),
|
||||
Color(0xFF73A7F9),
|
||||
Color(0xFF4285F4)
|
||||
)
|
||||
)
|
||||
Spacer(Modifier.height(16.dp))
|
||||
Text(
|
||||
text = stringResource(messageRes),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
fontSize = 15.sp
|
||||
)
|
||||
}
|
||||
|
||||
// CircularProgressIndicator(
|
||||
// modifier = Modifier.size(48.dp),
|
||||
// strokeWidth = 4.dp,
|
||||
// color = MaterialTheme.colorScheme.primary
|
||||
// )
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RotatingGradientRing(
|
||||
size: Dp,
|
||||
stroke: Dp,
|
||||
gradientColors: List<Color>,
|
||||
) {
|
||||
val rotation by rememberInfiniteTransition(label = "").animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 360f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
animation = tween(1200, easing = LinearEasing)
|
||||
), label = ""
|
||||
)
|
||||
|
||||
Canvas(
|
||||
modifier = Modifier
|
||||
.size(size)
|
||||
.graphicsLayer(rotationZ = rotation)
|
||||
) {
|
||||
drawCircle(
|
||||
brush = Brush.sweepGradient(gradientColors),
|
||||
style = Stroke(width = stroke.toPx(), cap = StrokeCap.Round)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewTaskTTLLoading() {
|
||||
var show by remember { mutableStateOf(true) }
|
||||
LaunchedEffect(Unit) {
|
||||
delay(3000)
|
||||
show = false
|
||||
}
|
||||
LoadingOverlay(visible = show)
|
||||
}
|
@@ -1,30 +0,0 @@
|
||||
package com.taskttl.core.ui
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
@Composable
|
||||
fun LoadingScreen() {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0x88000000))
|
||||
// 拦截所有点击事件,防止点击到底层
|
||||
.clickable(
|
||||
indication = null, // 无点击动画
|
||||
interactionSource = remember { MutableInteractionSource() }
|
||||
) { },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator()
|
||||
}
|
||||
}
|
@@ -0,0 +1,22 @@
|
||||
package com.taskttl.core.utils
|
||||
|
||||
/**
|
||||
* 外部应用程序启动器
|
||||
* @author DevTTL
|
||||
* @date 2025/03/08
|
||||
* @constructor 创建[ExternalAppLauncher]
|
||||
*/
|
||||
expect object ExternalAppLauncher {
|
||||
|
||||
/**
|
||||
* 打开应用程序评级
|
||||
*/
|
||||
suspend fun openAppRating()
|
||||
|
||||
/**
|
||||
* 打开网址
|
||||
* @param [url] 网址
|
||||
*/
|
||||
suspend fun openUrl(url: String)
|
||||
|
||||
}
|
@@ -0,0 +1,20 @@
|
||||
package com.taskttl.core.utils
|
||||
|
||||
/**
|
||||
* 本地化
|
||||
* @author DevTTL
|
||||
* @date 2025/10/14
|
||||
*/
|
||||
expect object Localization {
|
||||
/**
|
||||
* 应用语言
|
||||
* @param [iso]
|
||||
*/
|
||||
fun applyLanguage(iso: String)
|
||||
|
||||
/**
|
||||
* 获取设备当前语言
|
||||
* @return 设备语言代码,例如"zh"、"en"等
|
||||
*/
|
||||
fun getDeviceLanguage(): String
|
||||
}
|
@@ -1,8 +1,5 @@
|
||||
package com.taskttl.core.utils
|
||||
|
||||
import io.ktor.client.plugins.logging.Logger
|
||||
|
||||
enum class LogLevel { DEBUG, INFO, WARN, ERROR }
|
||||
|
||||
/**
|
||||
* 日志工具类
|
||||
@@ -14,4 +11,6 @@ expect object LogUtils {
|
||||
fun i(tag: String, message: String)
|
||||
fun w(tag: String, message: String)
|
||||
fun e(tag: String, message: String, throwable: Throwable? = null)
|
||||
}
|
||||
}
|
||||
|
||||
// enum class LogLevel { DEBUG, INFO, WARN, ERROR }
|
@@ -8,6 +8,7 @@ import com.taskttl.data.viewmodel.CategoryViewModel
|
||||
import com.taskttl.data.viewmodel.CountdownViewModel
|
||||
import com.taskttl.data.viewmodel.FeedbackViewModel
|
||||
import com.taskttl.data.viewmodel.OnboardingViewModel
|
||||
import com.taskttl.data.viewmodel.SettingsViewModel
|
||||
import com.taskttl.data.viewmodel.SplashViewModel
|
||||
import com.taskttl.data.viewmodel.TaskViewModel
|
||||
import org.koin.core.KoinApplication
|
||||
@@ -44,5 +45,6 @@ val viewModelModule = module {
|
||||
viewModelOf(::TaskViewModel)
|
||||
viewModelOf(::CategoryViewModel)
|
||||
viewModelOf(::CountdownViewModel)
|
||||
viewModelOf(::SettingsViewModel)
|
||||
viewModelOf(::FeedbackViewModel)
|
||||
}
|
@@ -3,18 +3,28 @@ package com.taskttl.data.repository.impl
|
||||
import com.taskttl.data.local.dao.CategoryDao
|
||||
import com.taskttl.data.local.dao.CountdownDao
|
||||
import com.taskttl.data.local.dao.TaskDao
|
||||
import com.taskttl.data.mapper.CategoryMapper
|
||||
import com.taskttl.data.local.model.Category
|
||||
import com.taskttl.data.local.model.CategoryColor
|
||||
import com.taskttl.data.local.model.CategoryIcon
|
||||
import com.taskttl.data.local.model.CategoryStatistics
|
||||
import com.taskttl.data.local.model.CategoryType
|
||||
import com.taskttl.data.mapper.CategoryMapper
|
||||
import com.taskttl.data.repository.CategoryRepository
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toLocalDateTime
|
||||
import org.jetbrains.compose.resources.getString
|
||||
import taskttl.composeapp.generated.resources.Res
|
||||
import taskttl.composeapp.generated.resources.category_birthday
|
||||
import taskttl.composeapp.generated.resources.category_book
|
||||
import taskttl.composeapp.generated.resources.category_briefcase
|
||||
import taskttl.composeapp.generated.resources.category_exam
|
||||
import taskttl.composeapp.generated.resources.category_festival
|
||||
import taskttl.composeapp.generated.resources.category_goal
|
||||
import taskttl.composeapp.generated.resources.category_heart
|
||||
import taskttl.composeapp.generated.resources.category_home
|
||||
import kotlin.time.Clock
|
||||
import kotlin.time.ExperimentalTime
|
||||
import kotlin.uuid.ExperimentalUuidApi
|
||||
@@ -123,7 +133,7 @@ class CategoryRepositoryImpl(
|
||||
val defaultTaskCategories = listOf(
|
||||
Category(
|
||||
id = Uuid.random().toString(),
|
||||
name = "工作",
|
||||
name = getString(Res.string.category_briefcase),
|
||||
color = CategoryColor.BLUE,
|
||||
icon = CategoryIcon.BRIEFCASE,
|
||||
type = CategoryType.TASK,
|
||||
@@ -132,7 +142,7 @@ class CategoryRepositoryImpl(
|
||||
),
|
||||
Category(
|
||||
id = Uuid.random().toString(),
|
||||
name = "生活",
|
||||
name = getString(Res.string.category_home),
|
||||
color = CategoryColor.GREEN,
|
||||
icon = CategoryIcon.HOME,
|
||||
type = CategoryType.TASK,
|
||||
@@ -141,7 +151,7 @@ class CategoryRepositoryImpl(
|
||||
),
|
||||
Category(
|
||||
id = Uuid.random().toString(),
|
||||
name = "健康",
|
||||
name = getString(Res.string.category_heart),
|
||||
color = CategoryColor.ORANGE,
|
||||
icon = CategoryIcon.HEART,
|
||||
type = CategoryType.TASK,
|
||||
@@ -150,7 +160,7 @@ class CategoryRepositoryImpl(
|
||||
),
|
||||
Category(
|
||||
id = Uuid.random().toString(),
|
||||
name = "学习",
|
||||
name = getString(Res.string.category_book),
|
||||
color = CategoryColor.PURPLE,
|
||||
icon = CategoryIcon.BOOK,
|
||||
type = CategoryType.TASK,
|
||||
@@ -163,7 +173,7 @@ class CategoryRepositoryImpl(
|
||||
val defaultCountdownCategories = listOf(
|
||||
Category(
|
||||
id = Uuid.random().toString(),
|
||||
name = "生日",
|
||||
name = getString(Res.string.category_birthday),
|
||||
color = CategoryColor.PINK,
|
||||
icon = CategoryIcon.BIRTHDAY,
|
||||
type = CategoryType.COUNTDOWN,
|
||||
@@ -172,7 +182,7 @@ class CategoryRepositoryImpl(
|
||||
),
|
||||
Category(
|
||||
id = Uuid.random().toString(),
|
||||
name = "节日",
|
||||
name = getString(Res.string.category_festival),
|
||||
color = CategoryColor.CYAN,
|
||||
icon = CategoryIcon.FESTIVAL,
|
||||
type = CategoryType.COUNTDOWN,
|
||||
@@ -181,7 +191,7 @@ class CategoryRepositoryImpl(
|
||||
),
|
||||
Category(
|
||||
id = Uuid.random().toString(),
|
||||
name = "目标",
|
||||
name = getString(Res.string.category_goal),
|
||||
color = CategoryColor.YELLOW,
|
||||
icon = CategoryIcon.GOAL,
|
||||
type = CategoryType.COUNTDOWN,
|
||||
@@ -190,7 +200,7 @@ class CategoryRepositoryImpl(
|
||||
),
|
||||
Category(
|
||||
id = Uuid.random().toString(),
|
||||
name = "考试",
|
||||
name = getString(Res.string.category_exam),
|
||||
color = CategoryColor.GREEN,
|
||||
icon = CategoryIcon.EXAM,
|
||||
type = CategoryType.COUNTDOWN,
|
||||
|
@@ -0,0 +1,63 @@
|
||||
package com.taskttl.data.state
|
||||
|
||||
/**
|
||||
* 设置状态
|
||||
* @author DevTTL
|
||||
* @date 2025/10/14
|
||||
* @constructor 创建[SettingsState]
|
||||
* @param [isLoading] 正在加载
|
||||
* @param [error] 错误
|
||||
*/
|
||||
data class SettingsState(
|
||||
val isLoading: Boolean = false,
|
||||
val error: String? = null,
|
||||
)
|
||||
|
||||
/**
|
||||
* 设置意图
|
||||
* @author DevTTL
|
||||
* @date 2025/10/14
|
||||
* @constructor 创建[SettingsIntent]
|
||||
*/
|
||||
sealed class SettingsIntent {
|
||||
/**
|
||||
* 打开应用评分
|
||||
* @author DevTTL
|
||||
* @date 2025/10/14
|
||||
*/
|
||||
object OpenAppRating: SettingsIntent()
|
||||
|
||||
/**
|
||||
* 打开网址
|
||||
* @author DevTTL
|
||||
* @date 2025/10/14
|
||||
* @constructor 创建[OpenUrl]
|
||||
* @param [url] 网址
|
||||
*/
|
||||
class OpenUrl(val url:String): SettingsIntent()
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置效果
|
||||
* @author DevTTL
|
||||
* @date 2025/10/14
|
||||
* @constructor 创建[SettingsEffect]
|
||||
*/
|
||||
sealed class SettingsEffect {
|
||||
/**
|
||||
* 导航返回
|
||||
* @author admin
|
||||
* @date 2025/10/12
|
||||
*/
|
||||
object NavigateBack : SettingsEffect()
|
||||
|
||||
/**
|
||||
* 显示消息
|
||||
* @author admin
|
||||
* @date 2025/10/12
|
||||
* @constructor 创建[ShowMessage]
|
||||
* @param [message] 消息
|
||||
*/
|
||||
data class ShowMessage(val message: String) : SettingsEffect()
|
||||
}
|
@@ -0,0 +1,58 @@
|
||||
package com.taskttl.data.viewmodel
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.taskttl.core.utils.ExternalAppLauncher
|
||||
import com.taskttl.data.state.SettingsEffect
|
||||
import com.taskttl.data.state.SettingsIntent
|
||||
import com.taskttl.data.state.SettingsState
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
/**
|
||||
* 反馈视图模型
|
||||
* @author admin
|
||||
* @date 2025/10/12
|
||||
* @constructor 创建[SettingsViewModel]
|
||||
*/
|
||||
class SettingsViewModel() : ViewModel() {
|
||||
|
||||
private val _state = MutableStateFlow(SettingsState())
|
||||
val state: StateFlow<SettingsState> = _state.asStateFlow()
|
||||
|
||||
private val _effects = MutableSharedFlow<SettingsEffect>()
|
||||
val effects: SharedFlow<SettingsEffect> = _effects.asSharedFlow()
|
||||
|
||||
|
||||
fun handleIntent(intent: SettingsIntent) {
|
||||
when (intent) {
|
||||
is SettingsIntent.OpenAppRating -> openAppRating()
|
||||
is SettingsIntent.OpenUrl -> openUrl(intent.url)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openAppRating() {
|
||||
viewModelScope.launch {
|
||||
ExternalAppLauncher.openAppRating()
|
||||
}
|
||||
}
|
||||
|
||||
private fun openUrl(url:String) {
|
||||
viewModelScope.launch {
|
||||
ExternalAppLauncher.openUrl(url)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 清除错误
|
||||
*/
|
||||
private fun clearError() {
|
||||
_state.value = _state.value.copy(error = null)
|
||||
}
|
||||
|
||||
}
|
@@ -17,9 +17,6 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.grid.GridCells
|
||||
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
|
||||
import androidx.compose.foundation.lazy.grid.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -48,7 +45,7 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.taskttl.core.ui.LoadingScreen
|
||||
import com.taskttl.core.ui.LoadingOverlay
|
||||
import com.taskttl.core.utils.ToastUtils
|
||||
import com.taskttl.data.local.model.Category
|
||||
import com.taskttl.data.local.model.CategoryColor
|
||||
@@ -56,7 +53,6 @@ import com.taskttl.data.local.model.CategoryIcon
|
||||
import com.taskttl.data.local.model.CategoryType
|
||||
import com.taskttl.data.state.CategoryEffect
|
||||
import com.taskttl.data.state.CategoryIntent
|
||||
import com.taskttl.data.state.TaskEffect
|
||||
import com.taskttl.data.viewmodel.CategoryViewModel
|
||||
import com.taskttl.ui.components.AppHeader
|
||||
import kotlinx.datetime.TimeZone
|
||||
@@ -89,7 +85,7 @@ import kotlin.uuid.Uuid
|
||||
fun CategoryEditScreen(
|
||||
categoryId: String? = null,
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: CategoryViewModel = koinViewModel()
|
||||
viewModel: CategoryViewModel = koinViewModel(),
|
||||
) {
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.effects.collect { effect ->
|
||||
@@ -173,6 +169,7 @@ fun CategoryEditScreen(
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(24.dp)
|
||||
) {
|
||||
@@ -257,7 +254,7 @@ fun CategoryEditScreen(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
CategoryIcon.entries.forEach {item ->
|
||||
CategoryIcon.entries.forEach { item ->
|
||||
IconOption(
|
||||
item = item,
|
||||
selected = icon == item,
|
||||
@@ -283,7 +280,7 @@ fun CategoryEditScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.isLoading) LoadingScreen()
|
||||
LoadingOverlay(state.isLoading)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +293,7 @@ private fun CategoryTypeOption(
|
||||
icon: ImageVector,
|
||||
text: String,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier
|
||||
@@ -367,7 +364,7 @@ private fun ColorOption(
|
||||
colorLong: CategoryColor,
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier
|
||||
modifier: Modifier,
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
|
@@ -56,7 +56,8 @@ import androidx.navigation.NavHostController
|
||||
import com.taskttl.core.routes.Routes.Main
|
||||
import com.taskttl.core.ui.ActionButtonListItem
|
||||
import com.taskttl.core.ui.ErrorDialog
|
||||
import com.taskttl.core.ui.LoadingScreen
|
||||
import com.taskttl.core.ui.LoadingOverlay
|
||||
|
||||
import com.taskttl.core.utils.ToastUtils
|
||||
import com.taskttl.data.local.model.Category
|
||||
import com.taskttl.data.local.model.CategoryType
|
||||
@@ -127,7 +128,7 @@ fun CategoryScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
|
||||
@@ -210,7 +211,7 @@ fun CategoryScreen(
|
||||
contentDescription = stringResource(Res.string.title_add_category)
|
||||
)
|
||||
}
|
||||
if (state.isLoading) LoadingScreen()
|
||||
LoadingOverlay(state.isLoading)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -20,6 +20,7 @@ import androidx.compose.material.icons.filled.CalendarToday
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
@@ -33,7 +34,8 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.taskttl.core.ui.Chip
|
||||
import com.taskttl.core.ui.LoadingScreen
|
||||
import com.taskttl.core.ui.LoadingOverlay
|
||||
|
||||
import com.taskttl.core.utils.DateUtils
|
||||
import com.taskttl.data.state.CountdownEffect
|
||||
import com.taskttl.data.viewmodel.CountdownViewModel
|
||||
@@ -104,7 +106,7 @@ fun CountdownDetailScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(16.dp)
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
@@ -234,7 +236,7 @@ fun CountdownDetailScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.isLoading) LoadingScreen()
|
||||
LoadingOverlay(state.isLoading)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.taskttl.presentation.countdown
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
@@ -40,7 +41,8 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.taskttl.core.ui.LoadingScreen
|
||||
import com.taskttl.core.ui.LoadingOverlay
|
||||
|
||||
import com.taskttl.core.utils.ToastUtils
|
||||
import com.taskttl.data.local.model.Category
|
||||
import com.taskttl.data.local.model.Countdown
|
||||
@@ -163,6 +165,7 @@ fun CountdownEditScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp),
|
||||
) {
|
||||
@@ -283,6 +286,6 @@ fun CountdownEditScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.isLoading) LoadingScreen()
|
||||
LoadingOverlay(state.isLoading)
|
||||
}
|
||||
}
|
||||
|
@@ -58,12 +58,10 @@ import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavHostController
|
||||
import com.taskttl.core.routes.Routes
|
||||
import com.taskttl.core.ui.ErrorDialog
|
||||
import com.taskttl.core.ui.LoadingScreen
|
||||
import com.taskttl.core.ui.LoadingOverlay
|
||||
import com.taskttl.core.utils.DateUtils
|
||||
import com.taskttl.core.utils.ToastUtils
|
||||
import com.taskttl.data.local.model.Countdown
|
||||
import com.taskttl.data.state.CategoryEffect
|
||||
import com.taskttl.data.state.CategoryIntent
|
||||
import com.taskttl.data.state.CountdownEffect
|
||||
import com.taskttl.data.state.CountdownIntent
|
||||
import com.taskttl.data.viewmodel.CountdownViewModel
|
||||
@@ -75,19 +73,18 @@ import org.koin.compose.viewmodel.koinViewModel
|
||||
import taskttl.composeapp.generated.resources.Res
|
||||
import taskttl.composeapp.generated.resources.delete
|
||||
import taskttl.composeapp.generated.resources.desc_add_countdown
|
||||
import taskttl.composeapp.generated.resources.edit
|
||||
import taskttl.composeapp.generated.resources.label_countdown_list
|
||||
import taskttl.composeapp.generated.resources.label_days
|
||||
import taskttl.composeapp.generated.resources.edit
|
||||
import taskttl.composeapp.generated.resources.text_add_countdown_tip
|
||||
import taskttl.composeapp.generated.resources.text_no_countdowns
|
||||
import taskttl.composeapp.generated.resources.title_countdown
|
||||
import taskttl.composeapp.generated.resources.title_edit_countdown
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun CountdownScreen(
|
||||
navController: NavHostController,
|
||||
viewModel: CountdownViewModel = koinViewModel()
|
||||
viewModel: CountdownViewModel = koinViewModel(),
|
||||
) {
|
||||
val state by viewModel.state.collectAsState()
|
||||
|
||||
@@ -97,6 +94,7 @@ fun CountdownScreen(
|
||||
is CountdownEffect.ShowMessage -> {
|
||||
ToastUtils.show(effect.message)
|
||||
}
|
||||
|
||||
is CountdownEffect.NavigateBack -> {
|
||||
navController.popBackStack()
|
||||
}
|
||||
@@ -125,7 +123,7 @@ fun CountdownScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
// 分类筛选
|
||||
@@ -220,7 +218,7 @@ fun CountdownScreen(
|
||||
contentDescription = stringResource(Res.string.desc_add_countdown)
|
||||
)
|
||||
}
|
||||
if (state.isLoading) LoadingScreen()
|
||||
LoadingOverlay(state.isLoading)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +227,7 @@ fun CountdownCard(
|
||||
countdown: Countdown,
|
||||
onEdit: () -> Unit = {},
|
||||
onDelete: () -> Unit = {},
|
||||
onCardClick: () -> Unit = {}
|
||||
onCardClick: () -> Unit = {},
|
||||
) {
|
||||
val countdownTime = DateUtils.calculateCountdownTime(countdown.targetDate)
|
||||
countdown.category
|
||||
@@ -422,7 +420,7 @@ fun IconBut(onClick: () -> Unit = {}, icon: ImageVector) {
|
||||
@Composable
|
||||
fun ReminderDialog(
|
||||
onDismiss: () -> Unit,
|
||||
onSave: (String) -> Unit
|
||||
onSave: (String) -> Unit,
|
||||
) {
|
||||
var isEnabled by remember { mutableStateOf(true) }
|
||||
var timeBefore by remember { mutableStateOf("1天前") }
|
||||
@@ -490,7 +488,7 @@ fun MoreActionsDialog(
|
||||
onDismiss: () -> Unit,
|
||||
onEdit: () -> Unit,
|
||||
onShare: () -> Unit,
|
||||
onDelete: () -> Unit
|
||||
onDelete: () -> Unit,
|
||||
) {
|
||||
AlertDialog(
|
||||
onDismissRequest = { onDismiss() },
|
||||
@@ -521,7 +519,7 @@ private fun ActionItem(
|
||||
text: String,
|
||||
icon: ImageVector,
|
||||
danger: Boolean = false,
|
||||
onClick: () -> Unit
|
||||
onClick: () -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
|
@@ -1,6 +1,7 @@
|
||||
package com.taskttl.presentation.settings
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -31,15 +32,20 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.taskttl.data.state.SettingsIntent
|
||||
import com.taskttl.data.viewmodel.SettingsViewModel
|
||||
import com.taskttl.ui.components.AppHeader
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import taskttl.composeapp.generated.resources.Res
|
||||
import taskttl.composeapp.generated.resources.all_rights_reserved
|
||||
import taskttl.composeapp.generated.resources.app_intro_content
|
||||
import taskttl.composeapp.generated.resources.app_intro_title
|
||||
import taskttl.composeapp.generated.resources.app_name
|
||||
import taskttl.composeapp.generated.resources.app_name_description
|
||||
import taskttl.composeapp.generated.resources.app_version_code
|
||||
import taskttl.composeapp.generated.resources.app_version_name
|
||||
import taskttl.composeapp.generated.resources.build_version
|
||||
import taskttl.composeapp.generated.resources.contact_us
|
||||
import taskttl.composeapp.generated.resources.copyright_year
|
||||
@@ -47,6 +53,7 @@ import taskttl.composeapp.generated.resources.developer_text
|
||||
import taskttl.composeapp.generated.resources.devttl_team
|
||||
import taskttl.composeapp.generated.resources.email
|
||||
import taskttl.composeapp.generated.resources.email_text
|
||||
import taskttl.composeapp.generated.resources.setting_privacy_email_uri
|
||||
import taskttl.composeapp.generated.resources.tech_stack
|
||||
import taskttl.composeapp.generated.resources.tech_stack_compose
|
||||
import taskttl.composeapp.generated.resources.tech_stack_kmp
|
||||
@@ -62,7 +69,8 @@ import taskttl.composeapp.generated.resources.web_url
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AboutScreen(
|
||||
onNavigateBack: () -> Unit
|
||||
onNavigateBack: () -> Unit,
|
||||
viewModel: SettingsViewModel = koinViewModel(),
|
||||
) {
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
Column(
|
||||
@@ -77,7 +85,7 @@ fun AboutScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
@@ -118,9 +126,15 @@ fun AboutScreen(
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
AboutInfoRow(labelRes = Res.string.version, value = "1.0.0")
|
||||
AboutInfoRow(
|
||||
labelRes = Res.string.version,
|
||||
valueRes = Res.string.app_version_name
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
AboutInfoRow(labelRes = Res.string.build_version, value = "1")
|
||||
AboutInfoRow(
|
||||
labelRes = Res.string.build_version,
|
||||
valueRes = Res.string.app_version_code
|
||||
)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
@@ -148,29 +162,29 @@ fun AboutScreen(
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
// 技术栈
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(Res.string.tech_stack),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
TechStackItem("Kotlin Multiplatform", Res.string.tech_stack_kmp)
|
||||
TechStackItem("Jetpack Compose", Res.string.tech_stack_compose)
|
||||
TechStackItem("Room Database", Res.string.tech_stack_room)
|
||||
TechStackItem("Koin", Res.string.tech_stack_koin)
|
||||
TechStackItem("Ktor", Res.string.tech_stack_ktor)
|
||||
TechStackItem("MVI Architecture", Res.string.tech_stack_mvi)
|
||||
}
|
||||
}
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
// Card(
|
||||
// modifier = Modifier.fillMaxWidth()
|
||||
// ) {
|
||||
// Column(
|
||||
// modifier = Modifier.padding(16.dp)
|
||||
// ) {
|
||||
// Text(
|
||||
// text = stringResource(Res.string.tech_stack),
|
||||
// style = MaterialTheme.typography.titleMedium,
|
||||
// fontWeight = FontWeight.Medium
|
||||
// )
|
||||
//
|
||||
// Spacer(modifier = Modifier.height(12.dp))
|
||||
//
|
||||
// TechStackItem("Kotlin Multiplatform", Res.string.tech_stack_kmp)
|
||||
// TechStackItem("Jetpack Compose", Res.string.tech_stack_compose)
|
||||
// TechStackItem("Room Database", Res.string.tech_stack_room)
|
||||
// TechStackItem("Koin", Res.string.tech_stack_koin)
|
||||
// TechStackItem("Ktor", Res.string.tech_stack_ktor)
|
||||
// TechStackItem("MVI Architecture", Res.string.tech_stack_mvi)
|
||||
// }
|
||||
// }
|
||||
// Spacer(modifier = Modifier.height(16.dp))
|
||||
// 开发者信息
|
||||
Card(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
@@ -208,18 +222,22 @@ fun AboutScreen(
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
val email = stringResource(Res.string.email)
|
||||
val emailUrl = stringResource(Res.string.setting_privacy_email_uri, email)
|
||||
ContactItem(
|
||||
icon = Icons.Default.Email,
|
||||
labelRes = Res.string.email_text,
|
||||
valueRes = Res.string.email
|
||||
valueRes = Res.string.email,
|
||||
onClick = { viewModel.handleIntent(SettingsIntent.OpenUrl(emailUrl)) }
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
val url = stringResource(Res.string.web_url)
|
||||
ContactItem(
|
||||
icon = Icons.Default.Language,
|
||||
labelRes = Res.string.web_text,
|
||||
valueRes = Res.string.web_url
|
||||
valueRes = Res.string.web_url,
|
||||
onClick = { viewModel.handleIntent(SettingsIntent.OpenUrl(url)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -243,7 +261,7 @@ fun AboutScreen(
|
||||
@Composable
|
||||
private fun AboutInfoRow(
|
||||
labelRes: StringResource,
|
||||
value: String
|
||||
valueRes: StringResource,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -255,7 +273,7 @@ private fun AboutInfoRow(
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
text = value,
|
||||
text = stringResource(valueRes),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
@@ -265,7 +283,7 @@ private fun AboutInfoRow(
|
||||
@Composable
|
||||
private fun TechStackItem(
|
||||
name: String,
|
||||
descriptionRes: StringResource
|
||||
descriptionRes: StringResource,
|
||||
) {
|
||||
Column {
|
||||
Text(
|
||||
@@ -286,10 +304,13 @@ private fun TechStackItem(
|
||||
private fun ContactItem(
|
||||
icon: ImageVector,
|
||||
labelRes: StringResource,
|
||||
valueRes: StringResource
|
||||
valueRes: StringResource,
|
||||
onClick: (() -> Unit)? = null,
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
.clickable(enabled = onClick != null) { onClick?.invoke() },
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Icon(
|
||||
imageVector = icon,
|
||||
|
@@ -100,7 +100,7 @@ fun DataManagementScreen(
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
|
@@ -42,7 +42,8 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.taskttl.core.domain.FeedbackType
|
||||
import com.taskttl.core.ui.ErrorDialog
|
||||
import com.taskttl.core.ui.LoadingScreen
|
||||
import com.taskttl.core.ui.LoadingOverlay
|
||||
|
||||
import com.taskttl.core.utils.ToastUtils
|
||||
import com.taskttl.data.network.domain.req.FeedbackReq
|
||||
import com.taskttl.data.state.FeedbackEffect
|
||||
@@ -110,7 +111,7 @@ fun FeedbackScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
@@ -218,7 +219,7 @@ fun FeedbackScreen(
|
||||
Spacer(modifier = Modifier.height(32.dp))
|
||||
}
|
||||
}
|
||||
if (state.isLoading) LoadingScreen()
|
||||
LoadingOverlay(state.isLoading)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -4,6 +4,7 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
@@ -29,7 +30,7 @@ fun PrivacyScreen(onNavigateBack: () -> Unit) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF5F5F5)),
|
||||
.background(MaterialTheme.colorScheme.background),
|
||||
) {
|
||||
DevTTLWebView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
|
@@ -24,6 +24,7 @@ import androidx.compose.material.icons.filled.Settings
|
||||
import androidx.compose.material.icons.filled.Share
|
||||
import androidx.compose.material.icons.filled.Storage
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -40,10 +41,14 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.navigation.NavHostController
|
||||
import com.taskttl.core.routes.Routes
|
||||
import com.taskttl.core.utils.ExternalAppLauncher
|
||||
import com.taskttl.data.state.SettingsIntent
|
||||
import com.taskttl.data.viewmodel.SettingsViewModel
|
||||
import com.taskttl.ui.components.AppHeader
|
||||
import org.jetbrains.compose.resources.StringResource
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import org.jetbrains.compose.ui.tooling.preview.Preview
|
||||
import org.koin.compose.viewmodel.koinViewModel
|
||||
import taskttl.composeapp.generated.resources.Res
|
||||
import taskttl.composeapp.generated.resources.section_data_management
|
||||
import taskttl.composeapp.generated.resources.section_general_settings
|
||||
@@ -65,6 +70,8 @@ import taskttl.composeapp.generated.resources.setting_language
|
||||
import taskttl.composeapp.generated.resources.setting_language_desc
|
||||
import taskttl.composeapp.generated.resources.setting_privacy_policy
|
||||
import taskttl.composeapp.generated.resources.setting_privacy_policy_desc
|
||||
import taskttl.composeapp.generated.resources.setting_privacy_rate
|
||||
import taskttl.composeapp.generated.resources.setting_privacy_rate_desc
|
||||
import taskttl.composeapp.generated.resources.setting_push_notification
|
||||
import taskttl.composeapp.generated.resources.setting_push_notification_desc
|
||||
import taskttl.composeapp.generated.resources.setting_share_achievement
|
||||
@@ -79,6 +86,7 @@ import taskttl.composeapp.generated.resources.title_app_settings
|
||||
@Preview
|
||||
fun SettingsScreen(
|
||||
navController: NavHostController,
|
||||
viewModel: SettingsViewModel = koinViewModel()
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
@@ -96,7 +104,7 @@ fun SettingsScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
) {
|
||||
@@ -218,13 +226,18 @@ fun SettingsScreen(
|
||||
showArrow = true,
|
||||
onClick = { navController.navigate(Routes.Main.Settings.Privacy) }
|
||||
)
|
||||
SettingItem(
|
||||
titleRes = Res.string.setting_privacy_rate,
|
||||
descriptionRes = Res.string.setting_privacy_rate_desc,
|
||||
showArrow = true,
|
||||
onClick = { viewModel.handleIntent(SettingsIntent.OpenAppRating) }
|
||||
)
|
||||
SettingItem(
|
||||
titleRes = Res.string.setting_about_app,
|
||||
descriptionRes = Res.string.setting_about_app_desc,
|
||||
showArrow = true,
|
||||
onClick = { navController.navigate(Routes.Main.Settings.About) }
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(24.dp))
|
||||
}
|
||||
}
|
||||
|
@@ -44,7 +44,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.navigation.NavHostController
|
||||
import com.taskttl.core.routes.Routes
|
||||
import com.taskttl.core.ui.Chip
|
||||
import com.taskttl.core.ui.LoadingScreen
|
||||
|
||||
import com.taskttl.data.local.model.Category
|
||||
import com.taskttl.data.state.CountdownIntent
|
||||
import com.taskttl.data.state.TaskIntent
|
||||
@@ -95,7 +95,7 @@ fun StatisticsScreen(
|
||||
LazyColumn(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
|
@@ -37,7 +37,8 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.taskttl.core.ui.LoadingScreen
|
||||
import com.taskttl.core.ui.LoadingOverlay
|
||||
|
||||
import com.taskttl.data.state.TaskEffect
|
||||
import com.taskttl.data.state.TaskIntent
|
||||
import com.taskttl.data.viewmodel.TaskViewModel
|
||||
@@ -111,6 +112,7 @@ fun TaskDetailScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.Top
|
||||
@@ -229,6 +231,6 @@ fun TaskDetailScreen(
|
||||
}
|
||||
|
||||
}
|
||||
if (state.isLoading) LoadingScreen()
|
||||
LoadingOverlay(state.isLoading)
|
||||
}
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.taskttl.presentation.task
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -39,7 +40,8 @@ import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.taskttl.core.routes.Routes
|
||||
import com.taskttl.core.ui.LoadingScreen
|
||||
import com.taskttl.core.ui.LoadingOverlay
|
||||
|
||||
import com.taskttl.core.utils.ToastUtils
|
||||
import com.taskttl.data.local.model.Category
|
||||
import com.taskttl.data.local.model.Task
|
||||
@@ -168,6 +170,7 @@ fun TaskEditorScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp),
|
||||
) {
|
||||
@@ -292,6 +295,6 @@ fun TaskEditorScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
if (state.isLoading) LoadingScreen()
|
||||
LoadingOverlay(state.isLoading)
|
||||
}
|
||||
}
|
||||
|
@@ -54,7 +54,8 @@ import androidx.navigation.NavHostController
|
||||
import com.taskttl.core.routes.Routes
|
||||
import com.taskttl.core.ui.ActionButtonListItem
|
||||
import com.taskttl.core.ui.ErrorDialog
|
||||
import com.taskttl.core.ui.LoadingScreen
|
||||
import com.taskttl.core.ui.LoadingOverlay
|
||||
|
||||
import com.taskttl.core.utils.ToastUtils
|
||||
import com.taskttl.data.local.model.Task
|
||||
import com.taskttl.data.state.TaskEffect
|
||||
@@ -129,7 +130,7 @@ fun TaskScreen(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFF5F5F5))
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(16.dp)
|
||||
) {
|
||||
if (state.isSearch) {
|
||||
@@ -258,7 +259,7 @@ fun TaskScreen(
|
||||
contentDescription = stringResource(Res.string.title_add_task)
|
||||
)
|
||||
}
|
||||
if (state.isLoading) LoadingScreen()
|
||||
LoadingOverlay(state.isLoading)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
@@ -39,16 +40,19 @@ fun AppHeader(
|
||||
showBack: Boolean = false,
|
||||
onBackClick: (() -> Unit)? = null,
|
||||
trailingIcon: ImageVector? = null,
|
||||
onTrailingClick: (() -> Unit)? = null
|
||||
onTrailingClick: (() -> Unit)? = null,
|
||||
) {
|
||||
val gradient = Brush.linearGradient(
|
||||
colors = listOf(
|
||||
MaterialTheme.colorScheme.primary,
|
||||
MaterialTheme.colorScheme.secondary
|
||||
)
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
Brush.linearGradient(
|
||||
colors = listOf(Color(0xFF667EEA), Color(0xFF764BA2))
|
||||
)
|
||||
)
|
||||
.background(brush = gradient)
|
||||
.padding(horizontal = 20.dp, vertical = 15.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
|
@@ -9,11 +9,11 @@ import androidx.compose.ui.graphics.Color
|
||||
|
||||
/** 浅色方案 */
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Color(0xFF6750A4),
|
||||
primary = Color(0xFF667EEA),
|
||||
onPrimary = Color(0xFFFFFFFF),
|
||||
primaryContainer = Color(0xFFEADDFF),
|
||||
onPrimaryContainer = Color(0xFF21005D),
|
||||
secondary = Color(0xFF625B71),
|
||||
secondary = Color(0xFF764BA2),
|
||||
onSecondary = Color(0xFFFFFFFF),
|
||||
secondaryContainer = Color(0xFFE8DEF8),
|
||||
onSecondaryContainer = Color(0xFF1D192B),
|
||||
@@ -25,7 +25,7 @@ private val LightColorScheme = lightColorScheme(
|
||||
onError = Color(0xFFFFFFFF),
|
||||
errorContainer = Color(0xFFFFDAD6),
|
||||
onErrorContainer = Color(0xFF410002),
|
||||
background = Color(0xFFFFFBFE),
|
||||
background = Color(0xFFF5F5F5),
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
|
@@ -0,0 +1,14 @@
|
||||
package com.taskttl.core.utils
|
||||
|
||||
import platform.Foundation.NSLocale
|
||||
|
||||
@Suppress("EXPECT_ACTUAL_CLASSIFIERS_ARE_IN_BETA_WARNING")
|
||||
actual object Localization {
|
||||
actual fun applyLanguage(iso: String) {
|
||||
NSUserDefaults.standardUserDefaults.setObject(arrayListOf(iso), "AppleLanguages")
|
||||
}
|
||||
|
||||
actual fun getDeviceLanguage(): String {
|
||||
return NSLocale.currentLocale.languageCode ?: "en"
|
||||
}
|
||||
}
|
@@ -37,8 +37,8 @@ android-compileSdk = "36"
|
||||
android-minSdk = "24"
|
||||
android-targetSdk = "36"
|
||||
|
||||
android-versionCode = "100000"
|
||||
android-versionName = "1.0.0"
|
||||
android-versionCode = "100001"
|
||||
android-versionName = "1.0.1"
|
||||
|
||||
android-facebookAppId = "1203530117944408"
|
||||
android-facebookClientToken = "1ee2da9430c1a589e8aa623bfaaaa586"
|
||||
|