From ea4abdea06803a376e05812c1f20cddc7f6acbeb Mon Sep 17 00:00:00 2001 From: Super12138 <70494801+Super12138@users.noreply.github.com> Date: Sat, 30 Dec 2023 18:43:22 +0800 Subject: [PATCH] First commit --- .github/workflows/android_ci.yml | 42 ++++ .gitignore | 2 + README.md | 2 +- app/.gitignore | 1 + app/build.gradle.kts | 72 +++++++ app/proguard-rules.pro | 21 ++ app/release/output-metadata.json | 20 ++ .../todo/ExampleInstrumentedTest.kt | 22 +++ app/src/main/AndroidManifest.xml | 42 ++++ .../cn/super12138/todo/ToDoApplication.kt | 23 +++ .../cn/super12138/todo/constant/Constants.kt | 11 ++ .../cn/super12138/todo/logic/Repository.kt | 20 ++ .../todo/logic/database/DBHelper.kt | 109 +++++++++++ .../todo/logic/database/SPHelper.kt | 11 ++ .../super12138/todo/logic/model/Progress.kt | 3 + .../cn/super12138/todo/logic/model/ToDo.kt | 19 ++ .../todo/logic/model/ToDoDatabase.kt | 28 +++ .../kotlin/cn/super12138/todo/utils/Toast.kt | 14 ++ .../todo/views/about/AboutActivity.kt | 77 ++++++++ .../todo/views/crash/CrashActivity.kt | 50 +++++ .../todo/views/crash/CrashHandler.kt | 28 +++ .../todo/views/main/MainActivity.kt | 76 +++++++ .../todo/views/progress/ProgressFragment.kt | 45 +++++ .../progress/ProgressFragmentViewModel.kt | 22 +++ .../todo/views/settings/SettingsActivity.kt | 62 ++++++ .../super12138/todo/views/todo/ToDoAdapter.kt | 118 +++++++++++ .../todo/views/todo/ToDoFragment.kt | 168 ++++++++++++++++ .../todo/views/todo/ToDoFragmentViewModel.kt | 10 + app/src/main/play_store_512.png | Bin 0 -> 52233 bytes app/src/main/res/drawable/add_24.xml | 10 + app/src/main/res/drawable/arrow_back_24.xml | 11 ++ app/src/main/res/drawable/check_24.xml | 10 + app/src/main/res/drawable/delete_24.xml | 10 + .../main/res/drawable/delete_forever_24.xml | 10 + app/src/main/res/drawable/exit_to_app_24.xml | 11 ++ app/src/main/res/drawable/github_24.xml | 10 + app/src/main/res/drawable/ic_launcher.xml | 10 + app/src/main/res/drawable/person_24.xml | 10 + app/src/main/res/drawable/settings_24.xml | 10 + app/src/main/res/drawable/update_24.xml | 10 + .../main/res/layout-land/activity_main.xml | 47 +++++ app/src/main/res/layout/activity_about.xml | 184 +++++++++++++++++ app/src/main/res/layout/activity_crash.xml | 60 ++++++ app/src/main/res/layout/activity_main.xml | 47 +++++ app/src/main/res/layout/activity_settings.xml | 41 ++++ app/src/main/res/layout/dialog_add_todo.xml | 108 ++++++++++ app/src/main/res/layout/fragment_progress.xml | 62 ++++++ app/src/main/res/layout/fragment_todo.xml | 60 ++++++ app/src/main/res/layout/todo_item.xml | 63 ++++++ app/src/main/res/menu/main.xml | 17 ++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 6 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 6 + app/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 4797 bytes .../mipmap-hdpi/ic_launcher_background.png | Bin 0 -> 854 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 8594 bytes .../mipmap-hdpi/ic_launcher_monochrome.png | Bin 0 -> 3739 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 6838 bytes .../ic_launcher_round_background.png | Bin 0 -> 854 bytes .../ic_launcher_round_foreground.png | Bin 0 -> 8594 bytes .../ic_launcher_round_monochrome.png | Bin 0 -> 3932 bytes app/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2630 bytes .../mipmap-mdpi/ic_launcher_background.png | Bin 0 -> 462 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 5244 bytes .../mipmap-mdpi/ic_launcher_monochrome.png | Bin 0 -> 2300 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 3883 bytes .../ic_launcher_round_background.png | Bin 0 -> 462 bytes .../ic_launcher_round_foreground.png | Bin 0 -> 5244 bytes .../ic_launcher_round_monochrome.png | Bin 0 -> 2349 bytes app/src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 6719 bytes .../mipmap-xhdpi/ic_launcher_background.png | Bin 0 -> 1319 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 12154 bytes .../mipmap-xhdpi/ic_launcher_monochrome.png | Bin 0 -> 5245 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 9947 bytes .../ic_launcher_round_background.png | Bin 0 -> 1319 bytes .../ic_launcher_round_foreground.png | Bin 0 -> 12154 bytes .../ic_launcher_round_monochrome.png | Bin 0 -> 5580 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 11578 bytes .../mipmap-xxhdpi/ic_launcher_background.png | Bin 0 -> 2951 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 20875 bytes .../mipmap-xxhdpi/ic_launcher_monochrome.png | Bin 0 -> 9390 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 17116 bytes .../ic_launcher_round_background.png | Bin 0 -> 2951 bytes .../ic_launcher_round_foreground.png | Bin 0 -> 20875 bytes .../ic_launcher_round_monochrome.png | Bin 0 -> 9780 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 17670 bytes .../mipmap-xxxhdpi/ic_launcher_background.png | Bin 0 -> 4234 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 30428 bytes .../mipmap-xxxhdpi/ic_launcher_monochrome.png | Bin 0 -> 13370 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 25156 bytes .../ic_launcher_round_background.png | Bin 0 -> 4234 bytes .../ic_launcher_round_foreground.png | Bin 0 -> 30428 bytes .../ic_launcher_round_monochrome.png | Bin 0 -> 13873 bytes app/src/main/res/values-night/colors.xml | 143 ++++++++++++++ .../main/res/values-night/theme_overlays.xml | 99 ++++++++++ app/src/main/res/values-night/themes.xml | 50 +++++ app/src/main/res/values-v23/themes.xml | 9 + app/src/main/res/values-zh-rCN/arrays.xml | 24 +++ app/src/main/res/values-zh-rCN/strings.xml | 42 ++++ app/src/main/res/values/arrays.xml | 24 +++ app/src/main/res/values/colors.xml | 147 ++++++++++++++ app/src/main/res/values/strings.xml | 42 ++++ app/src/main/res/values/theme_overlays.xml | 99 ++++++++++ app/src/main/res/values/themes.xml | 52 +++++ app/src/main/res/xml/backup_rules.xml | 13 ++ .../main/res/xml/data_extraction_rules.xml | 19 ++ app/src/main/res/xml/preferences.xml | 32 +++ .../cn/super12138/todo/ExampleUnitTest.kt | 16 ++ build.gradle.kts | 5 + gradle.properties | 23 +++ gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 59203 bytes gradle/wrapper/gradle-wrapper.properties | 6 + gradlew | 185 ++++++++++++++++++ gradlew.bat | 89 +++++++++ settings.gradle.kts | 17 ++ 114 files changed, 3066 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/android_ci.yml create mode 100644 app/.gitignore create mode 100644 app/build.gradle.kts create mode 100644 app/proguard-rules.pro create mode 100644 app/release/output-metadata.json create mode 100644 app/src/androidTest/kotlin/cn/super12138/todo/ExampleInstrumentedTest.kt create mode 100644 app/src/main/AndroidManifest.xml create mode 100644 app/src/main/kotlin/cn/super12138/todo/ToDoApplication.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/constant/Constants.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/logic/Repository.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/logic/database/DBHelper.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/logic/database/SPHelper.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/logic/model/Progress.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/logic/model/ToDo.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/logic/model/ToDoDatabase.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/utils/Toast.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/views/about/AboutActivity.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/views/crash/CrashActivity.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/views/crash/CrashHandler.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/views/main/MainActivity.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/views/progress/ProgressFragment.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/views/progress/ProgressFragmentViewModel.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/views/settings/SettingsActivity.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoAdapter.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoFragment.kt create mode 100644 app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoFragmentViewModel.kt create mode 100644 app/src/main/play_store_512.png create mode 100644 app/src/main/res/drawable/add_24.xml create mode 100644 app/src/main/res/drawable/arrow_back_24.xml create mode 100644 app/src/main/res/drawable/check_24.xml create mode 100644 app/src/main/res/drawable/delete_24.xml create mode 100644 app/src/main/res/drawable/delete_forever_24.xml create mode 100644 app/src/main/res/drawable/exit_to_app_24.xml create mode 100644 app/src/main/res/drawable/github_24.xml create mode 100644 app/src/main/res/drawable/ic_launcher.xml create mode 100644 app/src/main/res/drawable/person_24.xml create mode 100644 app/src/main/res/drawable/settings_24.xml create mode 100644 app/src/main/res/drawable/update_24.xml create mode 100644 app/src/main/res/layout-land/activity_main.xml create mode 100644 app/src/main/res/layout/activity_about.xml create mode 100644 app/src/main/res/layout/activity_crash.xml create mode 100644 app/src/main/res/layout/activity_main.xml create mode 100644 app/src/main/res/layout/activity_settings.xml create mode 100644 app/src/main/res/layout/dialog_add_todo.xml create mode 100644 app/src/main/res/layout/fragment_progress.xml create mode 100644 app/src/main/res/layout/fragment_todo.xml create mode 100644 app/src/main/res/layout/todo_item.xml create mode 100644 app/src/main/res/menu/main.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_background.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_monochrome.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round_background.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round_foreground.png create mode 100644 app/src/main/res/mipmap-hdpi/ic_launcher_round_monochrome.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_background.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_monochrome.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round_background.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round_foreground.png create mode 100644 app/src/main/res/mipmap-mdpi/ic_launcher_round_monochrome.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_background.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_monochrome.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round_background.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round_foreground.png create mode 100644 app/src/main/res/mipmap-xhdpi/ic_launcher_round_monochrome.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_background.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_monochrome.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round_background.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round_foreground.png create mode 100644 app/src/main/res/mipmap-xxhdpi/ic_launcher_round_monochrome.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_background.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_monochrome.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_background.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_foreground.png create mode 100644 app/src/main/res/mipmap-xxxhdpi/ic_launcher_round_monochrome.png create mode 100644 app/src/main/res/values-night/colors.xml create mode 100644 app/src/main/res/values-night/theme_overlays.xml create mode 100644 app/src/main/res/values-night/themes.xml create mode 100644 app/src/main/res/values-v23/themes.xml create mode 100644 app/src/main/res/values-zh-rCN/arrays.xml create mode 100644 app/src/main/res/values-zh-rCN/strings.xml create mode 100644 app/src/main/res/values/arrays.xml create mode 100644 app/src/main/res/values/colors.xml create mode 100644 app/src/main/res/values/strings.xml create mode 100644 app/src/main/res/values/theme_overlays.xml create mode 100644 app/src/main/res/values/themes.xml create mode 100644 app/src/main/res/xml/backup_rules.xml create mode 100644 app/src/main/res/xml/data_extraction_rules.xml create mode 100644 app/src/main/res/xml/preferences.xml create mode 100644 app/src/test/kotlin/cn/super12138/todo/ExampleUnitTest.kt create mode 100644 build.gradle.kts create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 settings.gradle.kts diff --git a/.github/workflows/android_ci.yml b/.github/workflows/android_ci.yml new file mode 100644 index 0000000..c4aa63d --- /dev/null +++ b/.github/workflows/android_ci.yml @@ -0,0 +1,42 @@ +name: Android CI +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4.1.1 + - name: set up JDK 17 + uses: actions/setup-java@v4.0.0 + with: + java-version: '17' + distribution: 'temurin' + cache: gradle + + - name: Grant execute permission for gradlew + run: chmod +x gradlew + + - name: Build with Gradle + run: ./gradlew assembleRelease + + - uses: ilharp/sign-android-release@nightly + name: Sign APK + id: sign_app + with: + releaseDir: app/build/outputs/apk/release + signingKey: ${{ secrets.ANDROID_SIGNING_KEY }} + keyAlias: ${{ secrets.ANDROID_KEY_ALIAS }} + keyStorePassword: ${{ secrets.ANDROID_KEYSTORE_PASSWORD }} + keyPassword: ${{ secrets.ANDROID_KEY_PASSWORD }} + buildToolsVersion: 33.0.0 + + - name: Upload APK + uses: actions/upload-artifact@v4.0.0 + with: + name: Release + path: ${{steps.sign_app.outputs.signedFile}} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 347e252..c4b6c3b 100644 --- a/.gitignore +++ b/.gitignore @@ -31,3 +31,5 @@ google-services.json # Android Profiling *.hprof + +.DS_Store \ No newline at end of file diff --git a/README.md b/README.md index f17a84f..4f34b01 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# ToDo +# 待办 | ToDo 简单的待办应用,遵循Material Design 3 diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..42afabf --- /dev/null +++ b/app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/app/build.gradle.kts b/app/build.gradle.kts new file mode 100644 index 0000000..44cb119 --- /dev/null +++ b/app/build.gradle.kts @@ -0,0 +1,72 @@ +plugins { + id("com.android.application") + id("org.jetbrains.kotlin.android") +} + +val baseVersionName = "1.0.0" +val commitHash by lazy { "git rev-parse --short HEAD".exec() } +val verCode by lazy { "git rev-list --count HEAD".exec().toInt() } + +android { + namespace = "cn.super12138.todo" + compileSdk = 34 + + defaultConfig { + applicationId = "cn.super12138.todo" + minSdk = 24 + targetSdk = 34 + versionCode = verCode + versionName = "${baseVersionName}-${commitHash}" + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + + buildTypes { + release { + isMinifyEnabled = true + isShrinkResources = true + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro" + ) + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_1_8 + targetCompatibility = JavaVersion.VERSION_1_8 + } + kotlinOptions { + jvmTarget = "1.8" + } + buildFeatures { + viewBinding = true + } +} + +dependencies { + + // AndroidX + implementation("androidx.core:core-ktx:1.12.0") + implementation("androidx.appcompat:appcompat:1.6.1") + implementation("androidx.constraintlayout:constraintlayout:2.1.4") + implementation("androidx.fragment:fragment:1.6.2") + implementation("androidx.fragment:fragment-ktx:1.6.2") + implementation("androidx.recyclerview:recyclerview:1.3.2") + implementation("androidx.lifecycle:lifecycle-viewmodel:2.6.2") + implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2") + implementation("androidx.preference:preference:1.2.1") + implementation("androidx.preference:preference-ktx:1.2.1") + // Material Design + implementation("com.google.android.material:material:1.12.0-alpha02") + // Test + testImplementation("junit:junit:4.13.2") + androidTestImplementation("androidx.test.ext:junit:1.1.5") + androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1") +} + +fun String.exec(): String = exec(this) + +@Suppress("UnstableApiUsage") +fun Project.exec(command: String): String = providers.exec { + commandLine(command.split(" ")) +}.standardOutput.asText.get().trim() \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/app/release/output-metadata.json b/app/release/output-metadata.json new file mode 100644 index 0000000..54a4fa5 --- /dev/null +++ b/app/release/output-metadata.json @@ -0,0 +1,20 @@ +{ + "version": 3, + "artifactType": { + "type": "APK", + "kind": "Directory" + }, + "applicationId": "cn.super12138.todo", + "variantName": "release", + "elements": [ + { + "type": "SINGLE", + "filters": [], + "attributes": [], + "versionCode": 1, + "versionName": "1.0.0", + "outputFile": "app-release.apk" + } + ], + "elementType": "File" +} \ No newline at end of file diff --git a/app/src/androidTest/kotlin/cn/super12138/todo/ExampleInstrumentedTest.kt b/app/src/androidTest/kotlin/cn/super12138/todo/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..1a9a74c --- /dev/null +++ b/app/src/androidTest/kotlin/cn/super12138/todo/ExampleInstrumentedTest.kt @@ -0,0 +1,22 @@ +package cn.super12138.todo + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.* +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("cn.super12138.todo", appContext.packageName) + } +} \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..b9d28c8 --- /dev/null +++ b/app/src/main/AndroidManifest.xml @@ -0,0 +1,42 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/ToDoApplication.kt b/app/src/main/kotlin/cn/super12138/todo/ToDoApplication.kt new file mode 100644 index 0000000..1b03ab0 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/ToDoApplication.kt @@ -0,0 +1,23 @@ +package cn.super12138.todo + +import android.annotation.SuppressLint +import android.app.Application +import android.content.Context +import cn.super12138.todo.views.crash.CrashHandler +import com.google.android.material.color.DynamicColors + +class ToDoApplication : Application() { + companion object { + @SuppressLint("StaticFieldLeak") + lateinit var context: Context + } + + override fun onCreate() { + super.onCreate() + DynamicColors.applyToActivitiesIfAvailable(this) + context = applicationContext + + val crashHandler = CrashHandler(this) + Thread.setDefaultUncaughtExceptionHandler(crashHandler) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/constant/Constants.kt b/app/src/main/kotlin/cn/super12138/todo/constant/Constants.kt new file mode 100644 index 0000000..ca57bd5 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/constant/Constants.kt @@ -0,0 +1,11 @@ +package cn.super12138.todo.constant + +object Constants { + const val DB_NAME = "ToDo.db" + const val DB_VERSION = 1 + const val TABLE_NAME = "ToDo" + + const val AUTHOR_GITHUB_URL = "https://github.com/Super12138/" + const val REPO_GITHUB_URL = "https://github.com/Super12138/ToDo" + const val UPDATE_URL = "https://github.com/Super12138/ToDo/releases" +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/Repository.kt b/app/src/main/kotlin/cn/super12138/todo/logic/Repository.kt new file mode 100644 index 0000000..13bbd7c --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/logic/Repository.kt @@ -0,0 +1,20 @@ +package cn.super12138.todo.logic + +import android.content.ContentValues +import android.content.Context +import cn.super12138.todo.logic.database.DBHelper +import cn.super12138.todo.logic.database.SPHelper + +object Repository { + fun getCompleteTotalCount() = DBHelper.getCompleteTotalCount() + + fun insertData(data: ContentValues) = DBHelper.insertData(data) + + fun deleteData(deleteAll: Boolean, uuid: String?) = DBHelper.deleteData(deleteAll, uuid) + + fun updateData(uuid: String, newData: ContentValues) = DBHelper.updateData(uuid, newData) + + fun getAllData() = DBHelper.getAllData() + + fun getPreferenceString(context: Context, key: String, defaultValue: String) = SPHelper.getPreferenceString(context, key, defaultValue) +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/database/DBHelper.kt b/app/src/main/kotlin/cn/super12138/todo/logic/database/DBHelper.kt new file mode 100644 index 0000000..569d9be --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/logic/database/DBHelper.kt @@ -0,0 +1,109 @@ +package cn.super12138.todo.logic.database + +import android.annotation.SuppressLint +import android.content.ContentValues +import cn.super12138.todo.ToDoApplication +import cn.super12138.todo.constant.Constants +import cn.super12138.todo.logic.model.Progress +import cn.super12138.todo.logic.model.ToDo +import cn.super12138.todo.logic.model.ToDoDatabase + +object DBHelper { + + fun insertData(data: ContentValues) { + ToDoDatabase( + ToDoApplication.context, + Constants.DB_NAME, + Constants.DB_VERSION + ).writableDatabase.use {db -> + db.insert(Constants.TABLE_NAME, null, data) + } + } + + fun deleteData(delAll: Boolean, uuid: String?) { + ToDoDatabase( + ToDoApplication.context, + Constants.DB_NAME, + Constants.DB_VERSION + ).writableDatabase.use { db -> + if (delAll) { + db.delete(Constants.TABLE_NAME, null, null) + } else { + if (uuid == null) { + throw RuntimeException("uuid cannot be null") + } + db.delete(Constants.TABLE_NAME, "uuid = ?", arrayOf(uuid)) + } + } + } + + fun updateData(uuid: String, newData: ContentValues) { + val dbHelper = ToDoDatabase( + ToDoApplication.context, + Constants.DB_NAME, + Constants.DB_VERSION + ).writableDatabase + dbHelper.update(Constants.TABLE_NAME, newData, "uuid = ?", arrayOf(uuid)) + } + + @SuppressLint("Range") + fun getCompleteTotalCount(): Progress { + var total = 0 + var complete = 0 + + ToDoDatabase( + ToDoApplication.context, + Constants.DB_NAME, + Constants.DB_VERSION + ).writableDatabase.use { db -> + db.query(Constants.TABLE_NAME, null, null, null, null, null, null, null).use { cursor -> + if (cursor.moveToFirst()) { + do { + val state = cursor.getString(cursor.getColumnIndex("state")) + if (state.toInt() == 1) { + complete += 1 + } + total += 1 + } while (cursor.moveToNext()) + cursor.close() + } + } + } + + return Progress(complete, total) + } + + @SuppressLint("Range") + fun getAllData() : MutableList { + val todoList = mutableListOf() + ToDoDatabase( + ToDoApplication.context, + Constants.DB_NAME, + Constants.DB_VERSION + ).writableDatabase.query( + Constants.TABLE_NAME, + null, + null, + null, + null, + null, + null, + null + ).use { cursor -> + if (cursor.moveToFirst()) { + do { + val uuid = cursor.getString(cursor.getColumnIndex("uuid")) + val subject = cursor.getString(cursor.getColumnIndex("subject")) + val state = cursor.getString(cursor.getColumnIndex("state")) + val todoContext = cursor.getString(cursor.getColumnIndex("context")) + + if (state.toInt() != 1) { + todoList.add(ToDo(uuid, todoContext, subject)) + } + } while (cursor.moveToNext()) + cursor.close() + } + } + return todoList + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/database/SPHelper.kt b/app/src/main/kotlin/cn/super12138/todo/logic/database/SPHelper.kt new file mode 100644 index 0000000..f3aab33 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/logic/database/SPHelper.kt @@ -0,0 +1,11 @@ +package cn.super12138.todo.logic.database + +import android.content.Context +import androidx.preference.PreferenceManager + +object SPHelper { + fun getPreferenceString(context: Context, name: String, defaultValue: String): String? { + val sharedPreferences = PreferenceManager.getDefaultSharedPreferences(context /* Activity context */) + return sharedPreferences.getString(name, defaultValue) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/model/Progress.kt b/app/src/main/kotlin/cn/super12138/todo/logic/model/Progress.kt new file mode 100644 index 0000000..897432c --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/logic/model/Progress.kt @@ -0,0 +1,3 @@ +package cn.super12138.todo.logic.model + +data class Progress(val complete: Int, val total: Int) diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/model/ToDo.kt b/app/src/main/kotlin/cn/super12138/todo/logic/model/ToDo.kt new file mode 100644 index 0000000..b670c7b --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/logic/model/ToDo.kt @@ -0,0 +1,19 @@ +package cn.super12138.todo.logic.model + +data class ToDo(val uuid: String, val context: String, val subject: String) + +/* +package cn.super12138.todo.logic.model + +import androidx.room.ColumnInfo +import androidx.room.Entity +import androidx.room.PrimaryKey + +@Entity(tableName = "todo") +data class ToDo ( + @PrimaryKey @ColumnInfo(name = "uuid") val uuid: String, + @ColumnInfo(name = "state") val state: Int, + @ColumnInfo(name = "subject") val subject: String, + @ColumnInfo(name = "context") val context: String +) + */ \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/logic/model/ToDoDatabase.kt b/app/src/main/kotlin/cn/super12138/todo/logic/model/ToDoDatabase.kt new file mode 100644 index 0000000..8e9d0d3 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/logic/model/ToDoDatabase.kt @@ -0,0 +1,28 @@ +package cn.super12138.todo.logic.model + +import android.content.Context +import android.database.sqlite.SQLiteDatabase +import android.database.sqlite.SQLiteOpenHelper + +class ToDoDatabase(val context: Context, name: String, version: Int) : + SQLiteOpenHelper(context, name, null, version) { + /* + * @param uuid: String 待办的uuid + * @param state: Int 待办的完成状态,0表示未完成,1表示完成 + * @param subject: String 待办的学科 + * @param context: String 待办的内容 + */ + private val createToDo = "create table ToDo (" + + "uuid text primary key," + + "state integer," + + "subject text," + + "context text)" + + override fun onCreate(db: SQLiteDatabase) { + db.execSQL(createToDo) + } + + override fun onUpgrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) { + + } +} diff --git a/app/src/main/kotlin/cn/super12138/todo/utils/Toast.kt b/app/src/main/kotlin/cn/super12138/todo/utils/Toast.kt new file mode 100644 index 0000000..04d9583 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/utils/Toast.kt @@ -0,0 +1,14 @@ +import android.content.Context +import android.widget.Toast +import cn.super12138.todo.ToDoApplication + +fun String.showToast( + context: Context = ToDoApplication.context, + duration: Int = Toast.LENGTH_SHORT +) { + Toast.makeText(context, this, duration).show() +} + +fun Int.showToast(context: Context = ToDoApplication.context, duration: Int = Toast.LENGTH_SHORT) { + Toast.makeText(context, this, duration).show() +} diff --git a/app/src/main/kotlin/cn/super12138/todo/views/about/AboutActivity.kt b/app/src/main/kotlin/cn/super12138/todo/views/about/AboutActivity.kt new file mode 100644 index 0000000..230a9f9 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/views/about/AboutActivity.kt @@ -0,0 +1,77 @@ +package cn.super12138.todo.views.about + +import android.content.Intent +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.view.WindowManager +import android.widget.Toast +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import cn.super12138.todo.constant.Constants +import cn.super12138.todo.databinding.ActivityAboutBinding +import showToast + +class AboutActivity : AppCompatActivity() { + private lateinit var binding: ActivityAboutBinding + private var clickCount = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + // 适配刘海屏 + val lp = window.attributes + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + lp.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + } + window.attributes = lp + + binding = ActivityAboutBinding.inflate(layoutInflater) + setContentView(binding.root) + + val pkgInfo = packageManager.getPackageInfo(packageName, 0) + val verName = pkgInfo.versionName + val verCode = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + pkgInfo.longVersionCode.toInt() + } else { + pkgInfo.versionCode + } + binding.appVersion.text = "$verName($verCode)" + + binding.toolbar.setNavigationOnClickListener { + finish() + } + + binding.checkUpdate.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(Constants.UPDATE_URL) + } + startActivity(intent) + } + + binding.openSource.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(Constants.REPO_GITHUB_URL) + } + startActivity(intent) + } + + + binding.developerInfo.setOnClickListener { + val intent = Intent(Intent.ACTION_VIEW).apply { + data = Uri.parse(Constants.AUTHOR_GITHUB_URL) + } + startActivity(intent) + } + + binding.appVersion.setOnClickListener { + clickCount++ + + if (clickCount == 5) { + clickCount = 0 + "🧧".showToast() + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/views/crash/CrashActivity.kt b/app/src/main/kotlin/cn/super12138/todo/views/crash/CrashActivity.kt new file mode 100644 index 0000000..1094b5d --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/views/crash/CrashActivity.kt @@ -0,0 +1,50 @@ +package cn.super12138.todo.views.crash + +import android.os.Build +import android.os.Bundle +import android.view.ViewGroup +import android.view.WindowManager +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.ViewCompat +import androidx.core.view.WindowInsetsCompat +import androidx.core.view.updateLayoutParams +import cn.super12138.todo.databinding.ActivityCrashBinding + +class CrashActivity : AppCompatActivity() { + private lateinit var binding: ActivityCrashBinding + + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + + // 适配刘海屏 + val lp = window.attributes + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + lp.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + } + window.attributes = lp + + binding = ActivityCrashBinding.inflate(layoutInflater) + setContentView(binding.root) + + ViewCompat.setOnApplyWindowInsetsListener(binding.exitApp) { view, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + view.updateLayoutParams { + leftMargin = insets.left + bottomMargin = insets.bottom + rightMargin = insets.right + } + + WindowInsetsCompat.CONSUMED + } + + val crashLogs = intent.getStringExtra("crash_logs") + binding.crashLog.text = crashLogs + + binding.exitApp.setOnClickListener { + this.finishAffinity() + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/views/crash/CrashHandler.kt b/app/src/main/kotlin/cn/super12138/todo/views/crash/CrashHandler.kt new file mode 100644 index 0000000..fa92578 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/views/crash/CrashHandler.kt @@ -0,0 +1,28 @@ +package cn.super12138.todo.views.crash + +import android.content.Context +import android.content.Intent +import android.os.Process + +class CrashHandler(private val context: Context) : Thread.UncaughtExceptionHandler { + + private val defaultUEH = Thread.getDefaultUncaughtExceptionHandler() + + override fun uncaughtException(thread: Thread, ex: Throwable) { + val stackTrace = ex.stackTraceToString() + + // 启动新的 Activity 来显示崩溃日志 + val intent = Intent(context, CrashActivity::class.java).apply { + flags = Intent.FLAG_ACTIVITY_NEW_TASK + putExtra("crash_logs", stackTrace) + } + context.startActivity(intent) + + // 杀掉崩溃的应用程序进程 + Process.killProcess(Process.myPid()) + System.exit(10) + + // 传递异常给默认的异常处理器 + defaultUEH?.uncaughtException(thread, ex) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/views/main/MainActivity.kt b/app/src/main/kotlin/cn/super12138/todo/views/main/MainActivity.kt new file mode 100644 index 0000000..0d0af3f --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/views/main/MainActivity.kt @@ -0,0 +1,76 @@ +package cn.super12138.todo.views.main +// 2023.11.18立项 +import android.content.Intent +import android.os.Build +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.WindowManager +import androidx.activity.enableEdgeToEdge +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AppCompatDelegate +import androidx.preference.PreferenceManager +import cn.super12138.todo.R +import cn.super12138.todo.ToDoApplication +import cn.super12138.todo.databinding.ActivityMainBinding +import cn.super12138.todo.logic.Repository +import cn.super12138.todo.views.settings.SettingsActivity + +class MainActivity : AppCompatActivity() { + private lateinit var binding: ActivityMainBinding + + override fun onCreate(savedInstanceState: Bundle?) { + enableEdgeToEdge() + super.onCreate(savedInstanceState) + // 适配刘海屏 + val lp = window.attributes + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + lp.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + } + window.attributes = lp + + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + setSupportActionBar(binding.toolbar) + /*val pref = getSharedPreferences("data",Context.MODE_PRIVATE) + val isFirstUse = pref.getBoolean("first_use", false) + + if (!isFirstUse) { + MaterialAlertDialogBuilder(this) + .setTitle("欢迎使用待办") + .setMessage("") + .setPositiveButton("确定") { dialog, which -> + val editor = getSharedPreferences("data", Context.MODE_PRIVATE).edit() + editor.putBoolean("first_use", true) + editor.apply() + } + .show() + } +*/ + val isDarkMode = Repository.getPreferenceString(this, "dark_mode", "0") + when (isDarkMode) { + "0" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + + "1" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + + "2" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + } + } + + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.main, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.item_settings -> { + val intent = Intent(ToDoApplication.context, SettingsActivity::class.java) + startActivity(intent) + } + } + return true + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/views/progress/ProgressFragment.kt b/app/src/main/kotlin/cn/super12138/todo/views/progress/ProgressFragment.kt new file mode 100644 index 0000000..1668d48 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/views/progress/ProgressFragment.kt @@ -0,0 +1,45 @@ +package cn.super12138.todo.views.progress + +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import cn.super12138.todo.databinding.FragmentProgressBinding + + +class ProgressFragment : Fragment() { + private lateinit var viewModel: ProgressFragmentViewModel + private lateinit var binding: FragmentProgressBinding + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentProgressBinding.inflate(inflater, container, false) + + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + viewModel = ViewModelProvider(requireActivity()).get(ProgressFragmentViewModel::class.java) + + viewModel.progress.observe(viewLifecycleOwner, Observer { value -> + binding.progressBar.setProgressCompat(value, true) + }) + + viewModel.totalCount.observe(viewLifecycleOwner, Observer { total -> + binding.totalTextView.text = total.toString() + }) + + viewModel.completeCount.observe(viewLifecycleOwner, Observer { complete -> + binding.completeTextView.text = complete.toString() + }) + + viewModel.updateProgress() + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/views/progress/ProgressFragmentViewModel.kt b/app/src/main/kotlin/cn/super12138/todo/views/progress/ProgressFragmentViewModel.kt new file mode 100644 index 0000000..8042096 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/views/progress/ProgressFragmentViewModel.kt @@ -0,0 +1,22 @@ +package cn.super12138.todo.views.progress + +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import cn.super12138.todo.logic.Repository + +class ProgressFragmentViewModel : ViewModel() { + val totalCount: MutableLiveData = MutableLiveData() + val completeCount: MutableLiveData = MutableLiveData() + val progress: MutableLiveData = MutableLiveData() + + fun updateProgress() { + val progressData = Repository.getCompleteTotalCount() + val total = progressData.total + val complete = progressData.complete + val calcProgress = (complete.toDouble() / total.toDouble()) * 100 + + progress.value = calcProgress.toInt() + totalCount.value = total + completeCount.value = complete + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/views/settings/SettingsActivity.kt b/app/src/main/kotlin/cn/super12138/todo/views/settings/SettingsActivity.kt new file mode 100644 index 0000000..679a967 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/views/settings/SettingsActivity.kt @@ -0,0 +1,62 @@ +package cn.super12138.todo.views.settings + +import android.os.Build +import android.os.Bundle +import android.view.WindowManager +import androidx.appcompat.app.AppCompatActivity +import androidx.appcompat.app.AppCompatDelegate +import androidx.preference.ListPreference +import androidx.preference.PreferenceFragmentCompat +import cn.super12138.todo.R +import cn.super12138.todo.databinding.ActivitySettingsBinding + +class SettingsActivity : AppCompatActivity() { + private lateinit var binding: ActivitySettingsBinding + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + // 适配刘海屏 + val lp = window.attributes + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + lp.layoutInDisplayCutoutMode = + WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES + } + window.attributes = lp + + binding = ActivitySettingsBinding.inflate(layoutInflater) + setContentView(binding.root) + + if (savedInstanceState == null) { + supportFragmentManager + .beginTransaction() + .replace(R.id.settings_frame, SettingsFragment()) + .commit() + } + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + binding.toolbar.setNavigationOnClickListener { + finish() + } + } + + + class SettingsFragment : PreferenceFragmentCompat() { + override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { + setPreferencesFromResource(R.xml.preferences, rootKey) + + findPreference("dark_mode")?.apply { + setOnPreferenceChangeListener { preference, newValue -> + when (newValue) { + "0" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + + "1" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) + + "2" -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) + } + activity?.recreate() + true + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoAdapter.kt b/app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoAdapter.kt new file mode 100644 index 0000000..48cdd5b --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoAdapter.kt @@ -0,0 +1,118 @@ +package cn.super12138.todo.views.todo + +import android.content.ContentValues +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Button +import android.widget.TextView +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelStoreOwner +import androidx.recyclerview.widget.RecyclerView +import cn.super12138.todo.R +import cn.super12138.todo.logic.Repository +import cn.super12138.todo.logic.model.ToDo +import cn.super12138.todo.views.progress.ProgressFragmentViewModel +import com.google.android.material.snackbar.Snackbar + +class ToDoAdapter(val todoList: MutableList, val viewModelStoreOwner: ViewModelStoreOwner) : + RecyclerView.Adapter() { + + inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val todoContext: TextView = view.findViewById(R.id.todo_context) + val todoSubject: TextView = view.findViewById(R.id.todo_subject) + val checkToDoBtn: Button = view.findViewById(R.id.check_item_btn) + val delToDoBtn: Button = view.findViewById(R.id.delete_item_btn) + } + + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val progressViewModel = + ViewModelProvider(viewModelStoreOwner).get(ProgressFragmentViewModel::class.java) + val todoViewModel = + ViewModelProvider(viewModelStoreOwner).get(ToDoFragmentViewModel::class.java) + + val view = LayoutInflater.from(parent.context) + .inflate(R.layout.todo_item, parent, false) + val holder = ViewHolder(view) + + holder.checkToDoBtn.setOnClickListener { + val position = holder.absoluteAdapterPosition + if (position < 0 || position >= todoList.size) { + return@setOnClickListener + } + val todo = todoList[position] + + todoList.removeAt(position) + notifyItemRemoved(position) + notifyItemRangeChanged(position, todoList.size) + + val newState = ContentValues().apply { + put("state", true) + } + Repository.updateData(todo.uuid, newState) + + progressViewModel.updateProgress() + + // 设置空项目提示可见性 + if (todoList.isEmpty()) { + todoViewModel.emptyTipVis.value = View.VISIBLE + } else { + todoViewModel.emptyTipVis.value = View.GONE + } + } + + holder.delToDoBtn.setOnClickListener { + val position = holder.absoluteAdapterPosition + if (position < 0 || position >= todoList.size) { + return@setOnClickListener + } + val todo = todoList[position] + + todoList.removeAt(position) + notifyItemRemoved(position) + notifyItemRangeChanged(position, todoList.size) + + val tempTaskInfo = ContentValues().apply { + put("uuid", todo.uuid) + put("state", false) + put("subject", todo.subject) + put("context", todo.context) + } + + Repository.deleteData(false, todo.uuid) + + progressViewModel.updateProgress() + + // 设置空项目提示可见性 + if (todoList.isEmpty()) { + todoViewModel.emptyTipVis.value = View.VISIBLE + } else { + todoViewModel.emptyTipVis.value = View.GONE + } + + Snackbar.make(it, R.string.task_deleted, Snackbar.LENGTH_LONG) + .setAction(R.string.delete_undo) { + if (todoList.size + 1 > 0) { + todoViewModel.emptyTipVis.value = View.GONE + } + todoList.add(ToDo(todo.uuid, todo.context, todo.subject)) + + todoViewModel.refreshData.value = 1 + Repository.insertData(tempTaskInfo) + progressViewModel.updateProgress() + } + .show() + } + return holder + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val todo = todoList[position] + holder.todoContext.text = todo.context + holder.todoSubject.text = todo.subject + + } + + override fun getItemCount() = todoList.size +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoFragment.kt b/app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoFragment.kt new file mode 100644 index 0000000..7358376 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoFragment.kt @@ -0,0 +1,168 @@ +package cn.super12138.todo.views.todo + +import android.content.ContentValues +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import androidx.lifecycle.Observer +import androidx.lifecycle.ViewModelProvider +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import cn.super12138.todo.R +import cn.super12138.todo.ToDoApplication +import cn.super12138.todo.databinding.DialogAddTodoBinding +import cn.super12138.todo.databinding.FragmentTodoBinding +import cn.super12138.todo.logic.Repository +import cn.super12138.todo.logic.model.ToDo +import cn.super12138.todo.views.progress.ProgressFragmentViewModel +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import java.util.UUID + +class ToDoFragment : Fragment() { + + private lateinit var binding: FragmentTodoBinding + private lateinit var ToDoDialogBinding: DialogAddTodoBinding + private val todoList = ArrayList() + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + binding = FragmentTodoBinding.inflate(inflater, container, false) + + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + val todos = Repository.getAllData() + for (todo in todos) { + todoList.add(ToDo(todo.uuid, todo.context, todo.subject)) + } + + /*ViewCompat.setOnApplyWindowInsetsListener(binding.todoList) { view, windowInsets -> + val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars()) + view.updateLayoutParams { + leftMargin = insets.left + bottomMargin = insets.bottom + rightMargin = insets.right + } + + WindowInsetsCompat.CONSUMED + }*/ + + val layoutManager = LinearLayoutManager(ToDoApplication.context) + binding.todoList.layoutManager = layoutManager + val adapter = ToDoAdapter(todoList, requireActivity()) + binding.todoList.adapter = adapter + + val progressViewModel = + ViewModelProvider(requireActivity()).get(ProgressFragmentViewModel::class.java) + val todoViewModel = + ViewModelProvider(requireActivity()).get(ToDoFragmentViewModel::class.java) + + + if (todoList.size == 0) { + todoViewModel.emptyTipVis.value = View.VISIBLE + } + binding.addItem.setOnClickListener { + ToDoDialogBinding = DialogAddTodoBinding.inflate(layoutInflater) + + activity?.let { it1 -> + MaterialAlertDialogBuilder(it1) + .setTitle(R.string.add_task) + .setView(ToDoDialogBinding.root) + .setPositiveButton(R.string.ok) { dialog, which -> + val randomUUID = UUID.randomUUID().toString() + val todoContext = ToDoDialogBinding.todoContext.editText?.text.toString() + val todoSubject = when (ToDoDialogBinding.todoSubject.checkedChipId) { + R.id.subject_chinese -> "语文" + R.id.subject_math -> "数学" + R.id.subject_english -> "英语" + R.id.subject_biology -> "生物" + R.id.subject_geography -> "地理" + R.id.subject_history -> "历史" + R.id.subject_physics -> "物理" + R.id.subject_law -> "道法" + else -> "未知" + } + + // 显示RecyclerView + if (todoList.size + 1 > 0) { + todoViewModel.emptyTipVis.value = View.GONE + } + + // 添加到RecyclerView + todoList.add( + ToDo(randomUUID, todoContext, todoSubject) + ) + + // 添加到数据库 + val todoData = ContentValues().apply { + put("uuid", randomUUID) + put("state", 0) + put("subject", todoSubject) + put("context", todoContext) + } + Repository.insertData(todoData) + // dbHelper.writableDatabase.insert("ToDo", null, todoData) + + binding.todoList.adapter?.notifyItemInserted(todoList.size + 1) + + progressViewModel.updateProgress() + } + .setNegativeButton(R.string.cancel, null) + .show() + } + } + + binding.addItem.setOnLongClickListener { + activity?.let { it1 -> + MaterialAlertDialogBuilder(it1) + .setTitle(R.string.warning) + .setMessage(R.string.delete_confirm) + .setPositiveButton(R.string.ok) { dialog, which -> + todoList.clear() + Repository.deleteData(true, null) + // dbHelper.writableDatabase.delete("ToDo", null, null) + binding.todoList.adapter?.notifyItemRangeRemoved(0, todoList.size + 1) + + progressViewModel.updateProgress() + + todoViewModel.emptyTipVis.value = View.VISIBLE + } + .setNegativeButton(R.string.cancel, null) + .show() + } + true + } + + binding.todoList.addOnScrollListener(object : RecyclerView.OnScrollListener() { + val fab = binding.addItem + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + if (dy > 0 && fab.isShown) { + fab.hide() + } + if (dy < 0 && !fab.isShown) { + fab.show() + } + } + }) + + todoViewModel.emptyTipVis.observe(viewLifecycleOwner, Observer { visibility -> + if (visibility == View.VISIBLE) { + binding.todoList.visibility = View.GONE + binding.emptyTip.visibility = View.VISIBLE + } else { + binding.todoList.visibility = View.VISIBLE + binding.emptyTip.visibility = View.GONE + } + }) + todoViewModel.refreshData.observe(viewLifecycleOwner, Observer { + binding.todoList.adapter?.notifyItemInserted(todoList.size + 1) + }) + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoFragmentViewModel.kt b/app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoFragmentViewModel.kt new file mode 100644 index 0000000..1902b67 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/views/todo/ToDoFragmentViewModel.kt @@ -0,0 +1,10 @@ +package cn.super12138.todo.views.todo + +import android.view.View +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel + +class ToDoFragmentViewModel : ViewModel() { + val emptyTipVis = MutableLiveData(View.GONE) + val refreshData = MutableLiveData(0) +} \ No newline at end of file diff --git a/app/src/main/play_store_512.png b/app/src/main/play_store_512.png new file mode 100644 index 0000000000000000000000000000000000000000..a1a9f3734099dc0ccaa71334fc412465bd6e02f9 GIT binary patch literal 52233 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable/add_24.xml b/app/src/main/res/drawable/add_24.xml new file mode 100644 index 0000000..a9503fd --- /dev/null +++ b/app/src/main/res/drawable/add_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/arrow_back_24.xml b/app/src/main/res/drawable/arrow_back_24.xml new file mode 100644 index 0000000..cd06f30 --- /dev/null +++ b/app/src/main/res/drawable/arrow_back_24.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/check_24.xml b/app/src/main/res/drawable/check_24.xml new file mode 100644 index 0000000..5623ef0 --- /dev/null +++ b/app/src/main/res/drawable/check_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/delete_24.xml b/app/src/main/res/drawable/delete_24.xml new file mode 100644 index 0000000..288869d --- /dev/null +++ b/app/src/main/res/drawable/delete_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/delete_forever_24.xml b/app/src/main/res/drawable/delete_forever_24.xml new file mode 100644 index 0000000..5615a26 --- /dev/null +++ b/app/src/main/res/drawable/delete_forever_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/exit_to_app_24.xml b/app/src/main/res/drawable/exit_to_app_24.xml new file mode 100644 index 0000000..348fab8 --- /dev/null +++ b/app/src/main/res/drawable/exit_to_app_24.xml @@ -0,0 +1,11 @@ + + + diff --git a/app/src/main/res/drawable/github_24.xml b/app/src/main/res/drawable/github_24.xml new file mode 100644 index 0000000..f32ae57 --- /dev/null +++ b/app/src/main/res/drawable/github_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/ic_launcher.xml b/app/src/main/res/drawable/ic_launcher.xml new file mode 100644 index 0000000..6e4f78d --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/person_24.xml b/app/src/main/res/drawable/person_24.xml new file mode 100644 index 0000000..74ba415 --- /dev/null +++ b/app/src/main/res/drawable/person_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/settings_24.xml b/app/src/main/res/drawable/settings_24.xml new file mode 100644 index 0000000..a7c7678 --- /dev/null +++ b/app/src/main/res/drawable/settings_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable/update_24.xml b/app/src/main/res/drawable/update_24.xml new file mode 100644 index 0000000..14c24e7 --- /dev/null +++ b/app/src/main/res/drawable/update_24.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml new file mode 100644 index 0000000..d5ea165 --- /dev/null +++ b/app/src/main/res/layout-land/activity_main.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml new file mode 100644 index 0000000..a15f87b --- /dev/null +++ b/app/src/main/res/layout/activity_about.xml @@ -0,0 +1,184 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_crash.xml b/app/src/main/res/layout/activity_crash.xml new file mode 100644 index 0000000..7edd0ee --- /dev/null +++ b/app/src/main/res/layout/activity_crash.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..b319978 --- /dev/null +++ b/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml new file mode 100644 index 0000000..0a79d29 --- /dev/null +++ b/app/src/main/res/layout/activity_settings.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/dialog_add_todo.xml b/app/src/main/res/layout/dialog_add_todo.xml new file mode 100644 index 0000000..5e35bb1 --- /dev/null +++ b/app/src/main/res/layout/dialog_add_todo.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_progress.xml b/app/src/main/res/layout/fragment_progress.xml new file mode 100644 index 0000000..64e65cb --- /dev/null +++ b/app/src/main/res/layout/fragment_progress.xml @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_todo.xml b/app/src/main/res/layout/fragment_todo.xml new file mode 100644 index 0000000..4f9a32d --- /dev/null +++ b/app/src/main/res/layout/fragment_todo.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/todo_item.xml b/app/src/main/res/layout/todo_item.xml new file mode 100644 index 0000000..6767100 --- /dev/null +++ b/app/src/main/res/layout/todo_item.xml @@ -0,0 +1,63 @@ + + + + + + + + + + + + +