├── .editorconfig
├── .github
├── ISSUE_TEMPLATE
│ ├── bug.yml
│ ├── config.yml
│ ├── fr.yml
│ └── other.yml
├── RELEASE_TEMPLATE.md
├── renovate.json
└── workflows
│ ├── code_quality.yml
│ ├── gradle-wrapper.yml
│ ├── nightly-merge.yml
│ ├── publish.yml
│ └── tasks.yml
├── .gitignore
├── .idea
├── .gitignore
├── .name
├── codeStyles
│ ├── Project.xml
│ └── codeStyleConfig.xml
├── compiler.xml
├── discord.xml
├── gradle.xml
├── icon.png
├── inspectionProfiles
│ └── Project_Default.xml
├── jsonSchemas.xml
├── kotlinc.xml
├── migrations.xml
└── misc.xml
├── Changelog.md
├── LICENSE
├── README.md
├── app
├── .gitignore
├── aapt2-resources.cfg
├── build.gradle.kts
├── proguard-rules.pro
└── src
│ ├── debug
│ ├── AndroidManifest.xml
│ └── res
│ │ └── xml
│ │ └── shortcuts.xml
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── com
│ │ │ └── qhy040404
│ │ │ └── libraryonetap
│ │ │ ├── LibraryOneTapApp.kt
│ │ │ ├── base
│ │ │ ├── BaseActivity.kt
│ │ │ ├── BaseFragment.kt
│ │ │ ├── BaseViewHolder.kt
│ │ │ └── IBinding.kt
│ │ │ ├── compat
│ │ │ ├── PMCompat.kt
│ │ │ └── WICompat.kt
│ │ │ ├── constant
│ │ │ ├── Constants.kt
│ │ │ ├── GlobalManager.kt
│ │ │ ├── GlobalValues.kt
│ │ │ ├── OnceTag.kt
│ │ │ ├── URLManager.kt
│ │ │ └── enums
│ │ │ │ ├── GPAAlgorithm.kt
│ │ │ │ ├── InsetsParams.kt
│ │ │ │ ├── OrderModes.kt
│ │ │ │ └── Parentheses.kt
│ │ │ ├── data
│ │ │ ├── CancelDTO.kt
│ │ │ ├── ElectricDTO.kt
│ │ │ ├── GithubAPIDTO.kt
│ │ │ ├── NetDTO.kt
│ │ │ ├── OrderListData.kt
│ │ │ ├── ReserveDTO.kt
│ │ │ ├── SessionDTO.kt
│ │ │ ├── VCardStatusDTO.kt
│ │ │ ├── VolunteerDTO.kt
│ │ │ └── tools
│ │ │ │ ├── Exam.kt
│ │ │ │ ├── Grade.kt
│ │ │ │ ├── Lesson.kt
│ │ │ │ └── Semester.kt
│ │ │ ├── recyclerview
│ │ │ ├── SimplePageActivity.kt
│ │ │ └── simplepage
│ │ │ │ ├── Card.kt
│ │ │ │ ├── CardView.kt
│ │ │ │ ├── CardViewBinder.kt
│ │ │ │ ├── Category.kt
│ │ │ │ ├── CategoryView.kt
│ │ │ │ ├── CategoryViewBinder.kt
│ │ │ │ ├── Clickable.kt
│ │ │ │ ├── ClickableView.kt
│ │ │ │ ├── ClickableViewBinder.kt
│ │ │ │ └── DividerItemDecoration.kt
│ │ │ ├── ui
│ │ │ ├── about
│ │ │ │ ├── AboutActivity.kt
│ │ │ │ ├── AbsAboutActivityProxy.java
│ │ │ │ ├── ClickableViewHolder.java
│ │ │ │ ├── ContributorViewBinder.java
│ │ │ │ ├── DividerItemDecoration.java
│ │ │ │ └── LicenseViewBinder.java
│ │ │ ├── dialog
│ │ │ │ └── ReserveDialog.kt
│ │ │ ├── fragment
│ │ │ │ ├── fullscreen
│ │ │ │ │ └── FullScreenDialogFragment.kt
│ │ │ │ ├── library
│ │ │ │ │ ├── SingleFragment.kt
│ │ │ │ │ └── YanxiujianFragment.kt
│ │ │ │ ├── settings
│ │ │ │ │ └── SettingsFragment.kt
│ │ │ │ └── tools
│ │ │ │ │ └── ToolsInitFragment.kt
│ │ │ ├── interfaces
│ │ │ │ └── IAppBarContainer.kt
│ │ │ ├── main
│ │ │ │ └── MainActivity.kt
│ │ │ └── tools
│ │ │ │ ├── BaseEduActivity.kt
│ │ │ │ ├── ExamsActivity.kt
│ │ │ │ ├── GradesActivity.kt
│ │ │ │ ├── LessonsActivity.kt
│ │ │ │ └── VCardActivity.kt
│ │ │ ├── utils
│ │ │ ├── AppUtils.kt
│ │ │ ├── CacheUtils.kt
│ │ │ ├── DownloadUtils.kt
│ │ │ ├── FileUtils.kt
│ │ │ ├── HashUtils.kt
│ │ │ ├── JsonUtils.kt
│ │ │ ├── MarkdownUtils.kt
│ │ │ ├── NetworkStateUtils.kt
│ │ │ ├── NotificationUtils.kt
│ │ │ ├── OsUtils.kt
│ │ │ ├── PackageUtils.kt
│ │ │ ├── PermissionUtils.kt
│ │ │ ├── QRUtils.kt
│ │ │ ├── RandomDataUtils.kt
│ │ │ ├── SPDelegates.kt
│ │ │ ├── SPUtils.kt
│ │ │ ├── TimeUtils.kt
│ │ │ ├── Toasty.kt
│ │ │ ├── UpdateUtils.kt
│ │ │ ├── encrypt
│ │ │ │ ├── AesEncryptUtils.kt
│ │ │ │ └── DesEncryptUtils.kt
│ │ │ ├── extensions
│ │ │ │ ├── AnyExtensions.kt
│ │ │ │ ├── ByteExtensions.kt
│ │ │ │ ├── ContextExtension.kt
│ │ │ │ ├── DoubleExtensions.kt
│ │ │ │ ├── FileExtensions.kt
│ │ │ │ ├── IntExtensions.kt
│ │ │ │ ├── StringExtension.kt
│ │ │ │ ├── UriExtensions.kt
│ │ │ │ └── ViewExtensions.kt
│ │ │ ├── lazy
│ │ │ │ └── ResettableLazy.kt
│ │ │ ├── library
│ │ │ │ ├── ReserveUtils.kt
│ │ │ │ └── RoomUtils.kt
│ │ │ ├── migration
│ │ │ │ ├── BaseMigration.kt
│ │ │ │ └── Migration.kt
│ │ │ ├── status
│ │ │ │ ├── AppStatusHelper.kt
│ │ │ │ └── OnAppStatusListener.kt
│ │ │ ├── tools
│ │ │ │ ├── GPAUtils.kt
│ │ │ │ ├── GetPortalData.kt
│ │ │ │ ├── GradesUtils.kt
│ │ │ │ └── VolunteerUtils.kt
│ │ │ └── web
│ │ │ │ ├── CookieJarImpl.kt
│ │ │ │ ├── Requests.kt
│ │ │ │ └── WebVPNUtils.kt
│ │ │ └── view
│ │ │ ├── FullScreenDialogView.kt
│ │ │ ├── PasswordPreference.kt
│ │ │ └── ToastView.kt
│ └── res
│ │ ├── drawable-night
│ │ └── bg_fs_dialog.xml
│ │ ├── drawable-xxxhdpi
│ │ ├── bg_toast.xml
│ │ └── pic_splash.webp
│ │ ├── drawable
│ │ ├── bg_fs_dialog.xml
│ │ ├── ic_about.xml
│ │ ├── ic_about_foreground.xml
│ │ ├── ic_cache.xml
│ │ ├── ic_card.xml
│ │ ├── ic_changelog.xml
│ │ ├── ic_color_palette.xml
│ │ ├── ic_dark_mode.xml
│ │ ├── ic_elec.xml
│ │ ├── ic_exams.xml
│ │ ├── ic_github.xml
│ │ ├── ic_gpa.xml
│ │ ├── ic_grades.xml
│ │ ├── ic_issue.xml
│ │ ├── ic_language.xml
│ │ ├── ic_launcher_foreground.xml
│ │ ├── ic_lessons.xml
│ │ ├── ic_material.xml
│ │ ├── ic_multi.xml
│ │ ├── ic_passwd.xml
│ │ ├── ic_refresh.xml
│ │ ├── ic_sc_exams.xml
│ │ ├── ic_sc_qr.xml
│ │ ├── ic_sc_tool.xml
│ │ ├── ic_sc_vcard.xml
│ │ ├── ic_single.xml
│ │ ├── ic_tools.xml
│ │ ├── ic_update.xml
│ │ ├── ic_userid.xml
│ │ ├── ic_volunteer.xml
│ │ ├── simplepage_card.xml
│ │ ├── splash_drawable.xml
│ │ └── white_back_btn.xml
│ │ ├── layout
│ │ ├── activity_main_bottom.xml
│ │ ├── activity_vcard.xml
│ │ ├── dialog_reserve.xml
│ │ ├── fragment_single.xml
│ │ ├── fragment_yanxiujian.xml
│ │ ├── preference_recyclerview.xml
│ │ └── simplepage_activity.xml
│ │ ├── menu
│ │ ├── bottom_nav_menu.xml
│ │ └── grade_menu.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xhdpi
│ │ ├── dcmt.webp
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_round.png
│ │ └── qhy040404_avatar.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.png
│ │ └── ic_launcher_round.png
│ │ ├── values-night-v31
│ │ └── colors.xml
│ │ ├── values-night
│ │ ├── color_fullscreen.xml
│ │ ├── colors.xml
│ │ ├── colors_simplepage.xml
│ │ ├── themes.xml
│ │ └── themes_custom.xml
│ │ ├── values-v31
│ │ └── colors.xml
│ │ ├── values-v34
│ │ └── bools.xml
│ │ ├── values-zh-rCN
│ │ ├── arrays.xml
│ │ ├── formats.xml
│ │ └── strings.xml
│ │ ├── values
│ │ ├── arrays.xml
│ │ ├── bools.xml
│ │ ├── color_custom.xml
│ │ ├── color_fullscreen.xml
│ │ ├── colors.xml
│ │ ├── colors_material.xml
│ │ ├── colors_simplepage.xml
│ │ ├── dimens.xml
│ │ ├── formats.xml
│ │ ├── ids.xml
│ │ ├── md_themes.xml
│ │ ├── strings.xml
│ │ ├── themes.xml
│ │ ├── themes_custom.xml
│ │ ├── themes_overlay.xml
│ │ └── untranslatable.xml
│ │ └── xml
│ │ ├── filepaths.xml
│ │ ├── network_security_config.xml
│ │ ├── root_preferences.xml
│ │ ├── shortcuts.xml
│ │ └── tools_list.xml
│ └── test
│ ├── README.md
│ └── java
│ └── com
│ └── qhy040404
│ └── libraryonetap
│ ├── AesTest.kt
│ ├── ConstantsTest.kt
│ ├── DatetimeTest.kt
│ └── DesTest.kt
├── build.gradle.kts
├── gradle.properties
├── gradle
├── libs.versions.toml
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── qodana.yaml
├── settings.gradle.kts
└── source
└── header.png
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | end_of_line = crlf
6 | indent_size = 2
7 | indent_style = space
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
11 | [*.java]
12 | ij_java_use_single_class_imports = true
13 |
14 | [*.{kt,kts}]
15 | ij_kotlin_imports_layout = *
16 | ij_kotlin_allow_trailing_comma = false
17 | ij_kotlin_allow_trailing_comma_on_call_site = false
18 | ktlint_code_style = intellij_idea
19 | ktlint_standard_property-naming = disabled
20 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: Create a report to help us improve
3 | labels: [ "awaiting assessment", "bug" ]
4 | title: "[BUG] "
5 | assignees: [ qhy040404 ]
6 | body:
7 | - type: dropdown
8 | id: channel
9 | attributes:
10 | label: Channel
11 | description: Channel can be found in about page.
12 | options:
13 | - Release
14 | - Pre-release
15 | - Debug
16 | validations:
17 | required: true
18 | - type: input
19 | id: version
20 | attributes:
21 | label: Version
22 | description: Version code that you are using.
23 | placeholder: |
24 | e.g. 3.4.0
25 | validations:
26 | required: true
27 | - type: textarea
28 | id: bug-description
29 | attributes:
30 | label: Bug description
31 | description: What's the bug?
32 | placeholder: |
33 | Application crashed and reporter says there's NPE.
34 | validations:
35 | required: true
36 | - type: textarea
37 | id: expected-behavior
38 | attributes:
39 | label: Expected behavior
40 | description: What did you expect to happen?
41 | placeholder: |
42 | Works fine?
43 | validations:
44 | required: true
45 | - type: textarea
46 | id: actual-behavior
47 | attributes:
48 | label: Actual behavior
49 | description: What happened instead?
50 | placeholder: |
51 | Crashed.
52 | validations:
53 | required: true
54 | - type: textarea
55 | id: steps-to-reproduce
56 | attributes:
57 | label: Steps to reproduce
58 | description: How to reproduce the bug.
59 | placeholder: |
60 | Just do your best. If you cannot reproduce, just left this blank.
61 | - type: input
62 | id: ui
63 | attributes:
64 | label: UI/OS
65 | description: Your system UI or OS.
66 | placeholder: MIUI / HarmonyOS / Native Android / etc.
67 | validations:
68 | required: true
69 | - type: input
70 | id: android
71 | attributes:
72 | label: Android Version
73 | description: Your Android Version
74 | placeholder: "13"
75 | validations:
76 | required: true
77 | - type: textarea
78 | id: additional-info
79 | attributes:
80 | label: Additional info
81 | description: Everything else you consider worthy that we didn't ask for.
82 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/fr.yml:
--------------------------------------------------------------------------------
1 | name: Feature Request
2 | description: Suggest an idea for this project
3 | labels: [ "awaiting assessment", "enhancement" ]
4 | title: "[FR] "
5 | assignees: [ qhy040404 ]
6 | body:
7 | - type: textarea
8 | id: propose
9 | attributes:
10 | label: Enhancement propose
11 | description: Propose of the enhancement.
12 | placeholder: |
13 | Show your idea here.
14 | validations:
15 | required: true
16 | - type: textarea
17 | id: solution
18 | attributes:
19 | label: Solution
20 | description: What's your solution for this enhancement?
21 | placeholder: |
22 | How to do it on your opinion? Or left this blank
23 | - type: textarea
24 | id: addition
25 | attributes:
26 | label: Additional info
27 | description: Everything else you consider worthy that we didn't ask for.
28 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/other.yml:
--------------------------------------------------------------------------------
1 | name: Other Suggestions
2 | description: Something else? Come here
3 | title: "[OTHER] "
4 | labels: [ "awaiting assessment" ]
5 | assignees: [ qhy040404 ]
6 | body:
7 | - type: textarea
8 | id: other
9 | attributes:
10 | label: Something
11 | description: Optimization? Or what
12 | placeholder: |
13 | I don't know how to write this placeholder. Just say anything.
14 | validations:
15 | required: true
16 |
--------------------------------------------------------------------------------
/.github/RELEASE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ## Changelog
2 |
3 | * This is automated GitHub deployment, human-readable changelog should be available soon.
4 |
5 | ---
6 |
7 | ### [Full Changelog](Changelog.md)
8 |
--------------------------------------------------------------------------------
/.github/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": [
4 | "config:base",
5 | ":assignee(qhy040404)",
6 | ":disableRateLimiting",
7 | ":label(automatic)"
8 | ],
9 | "packageRules": [
10 | {
11 | "matchUpdateTypes": ["patch", "pin", "digest"],
12 | "automerge": true
13 | }
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/.github/workflows/code_quality.yml:
--------------------------------------------------------------------------------
1 | name: Qodana
2 | on:
3 | workflow_dispatch:
4 | push:
5 | paths-ignore:
6 | - '**.png'
7 | - '**.jpg'
8 | - '.gitattributes'
9 | - '.github/**.json'
10 | - '.gitignore'
11 | - '.gitmodules'
12 | - '**.md'
13 | - 'LICENSE'
14 | - 'NOTICE'
15 | - '.github/workflows/tasks.yml'
16 | - '.github/workflows/publish.yml'
17 | - '.github/workflows/nightly-merge.yml'
18 |
19 | env:
20 | BUGLY_APPID: ${{ secrets.BUGLY_APPID }}
21 |
22 | jobs:
23 | qodana:
24 | runs-on: ubuntu-20.04
25 | steps:
26 | - uses: actions/checkout@v4.1.7
27 | with:
28 | fetch-depth: 0
29 |
30 | - name: Skip duplicate actions
31 | uses: fkirc/skip-duplicate-actions@v5.3.1
32 | with:
33 | cancel_others: true
34 |
35 | - name: Set up java
36 | uses: actions/setup-java@v4.3.0
37 | with:
38 | distribution: 'zulu'
39 | java-version: 17
40 |
41 | - name: Gradle cache
42 | uses: gradle/actions/setup-gradle@v4
43 |
44 | - run: ./gradlew build
45 |
46 | - name: 'Run Qodana'
47 | uses: JetBrains/qodana-action@v2024.1.9
48 | with:
49 | args: >
50 | --env,BUGLY_APPID=${{ secrets.BUGLY_APPID }}
51 | use-caches: false
52 |
53 | - name: Deploy to GitHub Pages
54 | if: github.ref_name == 'master'
55 | uses: peaceiris/actions-gh-pages@v4.0.0
56 | with:
57 | github_token: ${{ secrets.GITHUB_TOKEN }}
58 | publish_dir: ${{ runner.temp }}/qodana/results/report
59 | destination_dir: ./
60 |
61 | - name: Deploy to GitHub code scanning
62 | if: github.ref_name == 'master'
63 | uses: github/codeql-action/upload-sarif@v3.26.7
64 | with:
65 | sarif_file: ${{ runner.temp }}/qodana/results/qodana.sarif.json
66 |
--------------------------------------------------------------------------------
/.github/workflows/gradle-wrapper.yml:
--------------------------------------------------------------------------------
1 |
2 | name: gradle-wrapper
3 |
4 | on:
5 | pull_request:
6 | paths:
7 | - 'gradlew'
8 | - 'gradlew.bat'
9 | - 'gradle/wrapper/'
10 |
11 | jobs:
12 | validate:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - uses: actions/checkout@v4
16 | - uses: gradle/actions/wrapper-validation@v4
--------------------------------------------------------------------------------
/.github/workflows/nightly-merge.yml:
--------------------------------------------------------------------------------
1 | name: 'Nightly Merge'
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *'
6 | workflow_dispatch:
7 |
8 | jobs:
9 | nightly-merge:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v4.1.7
14 | with:
15 | fetch-depth: 0
16 |
17 | - name: Nightly Merge
18 | uses: robotology/gh-action-nightly-merge@v1.5.2
19 | with:
20 | stable_branch: 'master'
21 | development_branch: '4.0-dev'
22 | allow_ff: false
23 | user_name: 'qhyBot'
24 | user_email: '104984534+qhyBot@users.noreply.github.com'
25 | push_token: 'QHYBOT_TOKEN'
26 | env:
27 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
28 | QHYBOT_TOKEN: ${{ secrets.QHYBOT_RELEASE_TOKEN }}
29 |
--------------------------------------------------------------------------------
/.github/workflows/tasks.yml:
--------------------------------------------------------------------------------
1 | name: Gradle tasks
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | task:
7 | description: 'Task'
8 | required: true
9 |
10 | env:
11 | BUGLY_APPID: ${{ secrets.BUGLY_APPID }}
12 |
13 | jobs:
14 | tasks:
15 | name: Gradle tasks
16 | runs-on: ubuntu-latest
17 | timeout-minutes: 10
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v4.1.7
21 |
22 | - name: Set up java
23 | uses: actions/setup-java@v4.3.0
24 | with:
25 | distribution: 'zulu'
26 | java-version: 17
27 |
28 | - name: Validate Gradle Wrapper
29 | uses: gradle/actions/wrapper-validation@v4
30 |
31 | - name: Gradle cache
32 | uses: gradle/actions/setup-gradle@v4
33 |
34 | - run: ./gradlew ${{ inputs.task }}
35 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Built application files
2 | *.apk
3 | *.ap_
4 | *.aab
5 |
6 | # Files for the ART/Dalvik VM
7 | *.dex
8 |
9 | # Java class files
10 | *.class
11 |
12 | # Generated files
13 | bin/
14 | gen/
15 | out/
16 | # Uncomment the following line in case you need and you don't have the release build type files in your app
17 | # release/
18 |
19 | # Gradle files
20 | .gradle/
21 | **/build/
22 |
23 | # Local configuration file (sdk path, etc)
24 | local.properties
25 |
26 | # Proguard folder generated by Eclipse
27 | proguard/
28 |
29 | # Log Files
30 | *.log
31 |
32 | # Android Studio Navigation editor temp files
33 | .navigation/
34 |
35 | # Android Studio captures folder
36 | captures/
37 |
38 | # IntelliJ
39 | *.iml
40 | # Android Studio 3 in .gitignore file.
41 | .idea/caches
42 | .idea/modules.xml
43 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
44 | .idea/navEditor.xml
45 | /.idea/caches
46 | /.idea/libraries
47 | /.idea/workspace.xml
48 | /.idea/navEditor.xml
49 | /.idea/assetWizardSettings.xml
50 |
51 | # Keystore files
52 | # Uncomment the following lines if you do not want to check your keystore files in.
53 | #*.jks
54 | #*.keystore
55 |
56 | # External native build folder generated in Android Studio 2.2 and later
57 | .externalNativeBuild
58 |
59 | # Google Services (e.g. APIs or Firebase)
60 | # google-services.json
61 |
62 | # Freeline
63 | freeline.py
64 | freeline/
65 | freeline_project_description.json
66 |
67 | # fastlane
68 | fastlane/report.xml
69 | fastlane/Preview.html
70 | fastlane/screenshots
71 | fastlane/test_output
72 | fastlane/readme.md
73 |
74 | # Version control
75 | vcs.xml
76 |
77 | # lint
78 | lint/intermediates/
79 | lint/generated/
80 | lint/outputs/
81 | lint/tmp/
82 | # lint/reports/
83 | app/.classpath
84 | app/.project
85 | app/release/**
86 | app/debug/**
87 |
88 | .project
89 | .settings/org.eclipse.buildship.core.prefs
90 | app/.settings/org.eclipse.buildship.core.prefs
91 | .DS_Store
92 |
93 | resources/
94 | DEPRECATED.txt
95 |
--------------------------------------------------------------------------------
/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 | /deploymentTargetDropDown.xml
5 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | Library-One-Tap
--------------------------------------------------------------------------------
/.idea/codeStyles/codeStyleConfig.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/discord.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/.idea/icon.png
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/jsonSchemas.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/.idea/kotlinc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/migrations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Library-One-Tap-Android
2 |
3 | 一个便于打开离退/暂离页面的小程序,逐渐在成长为一个大工人的小工具箱
4 |
5 | 
6 |
7 | ---
8 |
9 | 
10 |
11 | [](https://www.codefactor.io/repository/github/qhy040404/Library-One-Tap-Android)
12 | [](https://github.com/qhy040404/Library-One-Tap-Android/commits)
13 | [](https://github.com/qhy040404/Library-One-Tap-Android/blob/master/LICENSE)
14 |
15 | [](https://github.com/qhy040404/Library-One-Tap-Android/releases/latest)
16 | [](https://github.com/qhy040404/Library-One-Tap-Android/releases/latest)
17 |
18 | [](https://github.com/qhy040404/Library-One-Tap-Android/releases)
19 | [](https://github.com/qhy040404/Library-One-Tap-Android/releases)
20 |
21 | 
22 |
23 | [Changelog](Changelog.md)
24 |
25 | ## 这是什么
26 |
27 | 这个程序最初只是为了在离开图书馆时快速展示离退码或者暂离码,随着慢慢的迭代,逐渐在成为一个适合大工人的工具箱
28 |
29 | ## 适配版本
30 |
31 | Android 10 ~ 13
32 |
33 | iOS 版本部分[源码][ios]在此,版本在 1.0 时期,由于申请 Apple Developer 既费时又费力还费钱,需要的自行完善代码后自签使用
34 |
35 | ## 功能
36 |
37 | * 单人座位签到码、离退码、暂离码
38 | * 研修间签到码
39 | * 工具箱
40 |
41 | ## 友链
42 |
43 | - [约座小程序](https://github.com/qhy040404/DLUT-library-auto-reservation)
44 | - [BeautyYuYanli/DLUT-login](https://github.com/BeautyYuYanli/DLUT-login) (Android 端的校园网登录思路来自这里)
45 |
46 | [ios]:https://github.com/qhy040404/Library-One-Tap-iOS
47 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /release
3 |
--------------------------------------------------------------------------------
/app/aapt2-resources.cfg:
--------------------------------------------------------------------------------
1 | color/material_orange_50#no_obfuscate
2 | color/material_indigo_50#no_obfuscate
3 | color/material_blue_grey_50#no_obfuscate
4 | color/material_teal_50#no_obfuscate
5 | color/material_cyan_50#no_obfuscate
6 | color/material_blue_50#no_obfuscate
7 | color/material_blue_grey_50#no_obfuscate
8 | color/material_deep_purple_50#no_obfuscate
9 |
10 | color/material_orange_100#no_obfuscate
11 | color/material_indigo_100#no_obfuscate
12 | color/material_blue_grey_100#no_obfuscate
13 | color/material_teal_100#no_obfuscate
14 | color/material_cyan_100#no_obfuscate
15 | color/material_blue_100#no_obfuscate
16 | color/material_blue_grey_100#no_obfuscate
17 | color/material_deep_purple_100#no_obfuscate
18 |
19 | color/material_orange_600#no_obfuscate
20 | color/material_indigo_600#no_obfuscate
21 | color/material_blue_grey_600#no_obfuscate
22 | color/material_teal_600#no_obfuscate
23 | color/material_cyan_600#no_obfuscate
24 | color/material_blue_600#no_obfuscate
25 | color/material_blue_grey_600#no_obfuscate
26 | color/material_deep_purple_600#no_obfuscate
--------------------------------------------------------------------------------
/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
11 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/debug/res/xml/shortcuts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
12 |
13 |
14 |
19 |
23 |
24 |
25 |
30 |
34 |
35 |
36 |
41 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.base
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.pm.ActivityInfo
5 | import android.content.res.Resources
6 | import android.graphics.Color
7 | import android.os.Bundle
8 | import android.view.MenuItem
9 | import androidx.viewbinding.ViewBinding
10 | import com.qhy040404.libraryonetap.LibraryOneTapApp
11 | import com.qhy040404.libraryonetap.R
12 | import com.qhy040404.libraryonetap.constant.GlobalValues
13 | import com.qhy040404.libraryonetap.utils.AppUtils
14 | import rikka.material.app.MaterialActivity
15 |
16 | abstract class BaseActivity : MaterialActivity(), IBinding {
17 | override lateinit var binding: VB
18 |
19 | override fun onCreate(savedInstanceState: Bundle?) {
20 | if (!GlobalValues.md3) {
21 | setTheme(AppUtils.getThemeID(GlobalValues.theme))
22 | }
23 |
24 | super.onCreate(savedInstanceState)
25 | LibraryOneTapApp.instance?.addActivity(this)
26 |
27 | binding = inflateBinding(layoutInflater).also {
28 | setContentView(it.root)
29 | }
30 |
31 | supportActionBar?.setDisplayHomeAsUpEnabled(true)
32 | init()
33 | }
34 |
35 | @SuppressLint("SourceLockedOrientationActivity")
36 | override fun onResume() {
37 | super.onResume()
38 | requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
39 | }
40 |
41 | override fun onDestroy() {
42 | super.onDestroy()
43 | LibraryOneTapApp.instance?.removeActivity(this)
44 | }
45 |
46 | override fun shouldApplyTranslucentSystemBars(): Boolean {
47 | return true
48 | }
49 |
50 | override fun computeUserThemeKey(): String? {
51 | return GlobalValues.darkMode + GlobalValues.theme + GlobalValues.md3
52 | }
53 |
54 | override fun onApplyTranslucentSystemBars() {
55 | super.onApplyTranslucentSystemBars()
56 | window.statusBarColor = Color.TRANSPARENT
57 | window.decorView.post {
58 | window.navigationBarColor = Color.TRANSPARENT
59 | window.isNavigationBarContrastEnforced = false
60 | }
61 | }
62 |
63 | override fun onApplyUserThemeResource(theme: Resources.Theme, isDecorView: Boolean) {
64 | theme.applyStyle(R.style.ThemeOverlay, true)
65 |
66 | if (!GlobalValues.md3) {
67 | theme.applyStyle(AppUtils.getThemeID(GlobalValues.theme), true)
68 | }
69 | }
70 |
71 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
72 | finish()
73 | return super.onOptionsItemSelected(item)
74 | }
75 |
76 | protected abstract fun init()
77 | }
78 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/base/BaseFragment.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.base
2 |
3 | import android.os.Bundle
4 | import android.view.LayoutInflater
5 | import android.view.View
6 | import android.view.ViewGroup
7 | import androidx.fragment.app.Fragment
8 | import androidx.viewbinding.ViewBinding
9 |
10 | abstract class BaseFragment : Fragment(), IBinding {
11 | private var _binding: VB? = null
12 |
13 | override val binding: VB
14 | get() = checkNotNull(_binding) { "Binding has been destroyed" }
15 |
16 | override fun onCreateView(
17 | inflater: LayoutInflater,
18 | container: ViewGroup?,
19 | savedInstanceState: Bundle?
20 | ): View {
21 | _binding = inflateBinding(layoutInflater)
22 | return binding.root
23 | }
24 |
25 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
26 | super.onViewCreated(view, savedInstanceState)
27 | init()
28 | }
29 |
30 | override fun onDestroyView() {
31 | _binding = null
32 | super.onDestroyView()
33 | }
34 |
35 | abstract fun init()
36 |
37 | /**
38 | * @see android.app.Activity.runOnUiThread
39 | */
40 | fun runOnUiThread(action: () -> Unit) = runCatching { requireActivity().runOnUiThread(action) }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/base/BaseViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.base
2 |
3 | import android.view.View
4 | import androidx.recyclerview.widget.RecyclerView
5 |
6 | class BaseViewHolder(view: View) : RecyclerView.ViewHolder(view)
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/base/IBinding.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.base
2 |
3 | import android.view.LayoutInflater
4 | import androidx.viewbinding.ViewBinding
5 | import java.lang.reflect.Method
6 | import java.lang.reflect.ParameterizedType
7 |
8 | internal sealed interface IBinding {
9 | val binding: VB
10 |
11 | fun inflateBinding(inflater: LayoutInflater): T {
12 | var method: Method?
13 | var clazz: Class<*> = javaClass
14 | while (clazz.superclass != null) {
15 | method = clazz.filterBindingMethod()
16 | if (method == null) {
17 | clazz = clazz.superclass
18 | } else {
19 | @Suppress("UNCHECKED_CAST")
20 | return method.invoke(null, inflater) as T
21 | }
22 | }
23 | error("No Binding type argument found.")
24 | }
25 |
26 | private fun Class<*>.filterBindingMethod(): Method? {
27 | return (genericSuperclass as? ParameterizedType)?.actualTypeArguments
28 | ?.asSequence()
29 | ?.filterIsInstance>()
30 | ?.firstOrNull { it.simpleName.endsWith("Binding") }
31 | ?.getDeclaredMethod("inflate", LayoutInflater::class.java)
32 | ?.also { it.isAccessible = true }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/compat/PMCompat.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.compat
2 |
3 | import android.content.pm.ApplicationInfo
4 | import android.content.pm.PackageInfo
5 | import android.content.pm.PackageManager
6 | import com.qhy040404.libraryonetap.LibraryOneTapApp
7 | import com.qhy040404.libraryonetap.utils.OsUtils
8 |
9 | /**
10 | * Modified Package Manager implementations
11 | */
12 | object PMCompat {
13 | private val packageManager by lazy { LibraryOneTapApp.app.packageManager }
14 |
15 | fun getPackageInfo(packageName: String, flags: Int): PackageInfo {
16 | return if (OsUtils.atLeastT()) {
17 | packageManager.getPackageInfo(
18 | packageName,
19 | PackageManager.PackageInfoFlags.of(flags.toLong())
20 | )
21 | } else {
22 | packageManager.getPackageInfo(packageName, flags)
23 | }
24 | }
25 |
26 | fun getApplicationInfo(packageName: String, flags: Int): ApplicationInfo {
27 | return if (OsUtils.atLeastT()) {
28 | packageManager.getApplicationInfo(
29 | packageName,
30 | PackageManager.ApplicationInfoFlags.of(flags.toLong())
31 | )
32 | } else {
33 | packageManager.getApplicationInfo(packageName, flags)
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/compat/WICompat.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.compat
2 |
3 | import androidx.core.view.WindowInsetsCompat
4 | import com.qhy040404.libraryonetap.constant.enums.InsetsParams
5 | import com.qhy040404.libraryonetap.utils.OsUtils
6 |
7 | /**
8 | * Modified Window Insets implementations
9 | */
10 | @Suppress("DEPRECATION")
11 | object WICompat {
12 | fun getSystemBars() = if (OsUtils.atLeastR()) {
13 | WindowInsetsCompat.Type.systemBars()
14 | } else {
15 | 0
16 | }
17 |
18 | fun getInsetsParam(
19 | windowInsets: WindowInsetsCompat,
20 | typeMask: Int,
21 | param: InsetsParams
22 | ) = if (OsUtils.atLeastR()) {
23 | when (param) {
24 | InsetsParams.LEFT -> windowInsets.getInsets(typeMask).left
25 | InsetsParams.RIGHT -> windowInsets.getInsets(typeMask).right
26 | InsetsParams.TOP -> windowInsets.getInsets(typeMask).top
27 | InsetsParams.BOTTOM -> windowInsets.getInsets(typeMask).bottom
28 | }
29 | } else {
30 | when (param) {
31 | InsetsParams.LEFT -> windowInsets.systemWindowInsetLeft
32 | InsetsParams.RIGHT -> windowInsets.systemWindowInsetRight
33 | InsetsParams.TOP -> windowInsets.systemWindowInsetTop
34 | InsetsParams.BOTTOM -> windowInsets.systemWindowInsetBottom
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/constant/Constants.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.constant
2 |
3 | object Constants {
4 | const val GLOBAL_ERROR = "Error"
5 | const val NET_TIMEOUT = "Timeout"
6 | const val NET_ERROR = "Network Unknown Error"
7 | const val NET_DISCONNECTED = "Offline"
8 | const val STRING_NULL = ""
9 |
10 | const val PREF_NAME = "name"
11 | const val PREF_ID = "userid"
12 | const val PREF_PASSWD = "passwd"
13 |
14 | const val PREF_GP = "gp_option"
15 |
16 | const val PREF_DARK = "dark"
17 | const val PREF_THEME = "theme"
18 | const val PREF_MD3 = "md3"
19 | const val PREF_LOCALE = "locale"
20 | const val PREF_RESET = "reset"
21 |
22 | const val PREF_CACHE = "cache"
23 | const val PREF_UPDATE = "update"
24 | const val PREF_CHANGELOG = "changelog"
25 | const val PREF_ISSUE = "issue"
26 | const val PREF_ABOUT = "about"
27 |
28 | const val LATEST_APK_NAME = "latestApkName"
29 |
30 | const val DEFAULT_GP = "dlut"
31 | const val DEFAULT_DARK = "system"
32 | const val DEFAULT_THEME = "library"
33 |
34 | const val CONTENT_TYPE_SSO = "application/x-www-form-urlencoded; charset=utf-8"
35 | const val CONTENT_TYPE_JSON = "application/json;charset=UTF-8"
36 | const val CONTENT_TYPE_VCARD = "application/x-www-form-urlencoded"
37 |
38 | const val SHORTCUT_DETAIL = "com.qhy040404.libraryonetap.intent.action.DETAIL"
39 | const val SHORTCUT_TOOLS = "com.qhy040404.libraryonetap.intent.action.TOOLS"
40 | const val SHORTCUT_VCARD = "com.qhy040404.libraryonetap.intent.action.VCARD"
41 | const val SHORTCUT_EXAMS = "com.qhy040404.libraryonetap.intent.action.EXAMS"
42 |
43 | const val LIBRARY_METHOD_IN = "in"
44 | const val LIBRARY_METHOD_OUT = "out"
45 | const val LIBRARY_METHOD_TEMP = "temp"
46 |
47 | const val GPA_DLUT = "dlut"
48 | const val GPA_STANDARD5 = "std5"
49 | const val GPA_STANDARD4 = "std4"
50 | const val GPA_PEKING4 = "peking4"
51 |
52 | const val PORTAL_DEFAULT_POST = "{}"
53 |
54 | const val TOOLS_NET = "net"
55 | const val TOOLS_ELECTRIC = "electric"
56 | const val TOOLS_VCARD = "vcard"
57 | const val TOOLS_VOLUNTEER = "volunteer"
58 | const val TOOLS_GRADES_MAJOR = "major"
59 | const val TOOLS_LESSONS = "lessons"
60 | const val TOOLS_EXAMS = "exams"
61 |
62 | const val RESERVE_VALID = "\"seat_type\":\"1\""
63 | const val RESERVE_HAS_PERSON = "\"seat_type\":3"
64 |
65 | const val CHANGELOG_INACTIVE = "changelog_html_inactive"
66 | const val CHANGELOG_ACTIVE = "changelog_html_active"
67 |
68 | const val LATEST_GRADE_ID = "latest_grade_id"
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/constant/GlobalManager.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.constant
2 |
3 | import com.qhy040404.libraryonetap.utils.lazy.resettableManager
4 | import com.squareup.moshi.Moshi
5 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
6 |
7 | object GlobalManager {
8 | val lazyMgr = resettableManager()
9 | val moshi: Moshi = Moshi.Builder()
10 | .addLast(KotlinJsonAdapterFactory())
11 | .build()
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/constant/OnceTag.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.constant
2 |
3 | object OnceTag {
4 | const val FIRST_DATA_INPUT = "InitializeSettings"
5 | const val CLEAR_AFTER_UPDATE = "RemoveApkAfterUpdate"
6 |
7 | const val APP_INITIALIZED = "AppInitialized"
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/constant/enums/GPAAlgorithm.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.constant.enums
2 |
3 | @Suppress("SpellCheckingInspection")
4 | enum class GPAAlgorithm {
5 | DLUT,
6 | STD5,
7 | STD4,
8 | PK4
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/constant/enums/InsetsParams.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.constant.enums
2 |
3 | enum class InsetsParams {
4 | LEFT,
5 | RIGHT,
6 | TOP,
7 | BOTTOM
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/constant/enums/OrderModes.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.constant.enums
2 |
3 | enum class OrderModes {
4 | YANXIUJIAN,
5 | DETAIL;
6 |
7 | override fun toString(): String {
8 | return (values().indexOf(this) + 1).toString()
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/constant/enums/Parentheses.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.constant.enums
2 |
3 | enum class Parentheses {
4 | SMALL,
5 | MEDIUM,
6 | LARGE
7 | }
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/CancelDTO.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data
2 |
3 | data class CancelDTO(
4 | val success: Boolean,
5 | val message: String? = null
6 | )
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/ElectricDTO.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data
2 |
3 | data class ElectricDTO(
4 | val dormitoryInfo_list: List? = null,
5 | val flag: String
6 | ) {
7 | @Suppress("SpellCheckingInspection")
8 | data class ElectricInnerDTO(
9 | val SSMC: String? = null,
10 | val ZSBH: String? = null,
11 | val flag: String,
12 | val resele: String? = null
13 | )
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/GithubAPIDTO.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data
2 |
3 | @Suppress("SpellCheckingInspection")
4 | data class GithubAPIDTO(
5 | val assets: List,
6 | val assets_url: String,
7 | val author: Author,
8 | val body: String,
9 | val created_at: String,
10 | val draft: Boolean,
11 | val html_url: String,
12 | val id: Int,
13 | val name: String,
14 | val node_id: String,
15 | val prerelease: Boolean,
16 | val published_at: String,
17 | val tag_name: String,
18 | val tarball_url: String,
19 | val target_commitish: String,
20 | val upload_url: String,
21 | val url: String,
22 | val zipball_url: String
23 | ) {
24 | data class Asset(
25 | val browser_download_url: String,
26 | val content_type: String,
27 | val created_at: String,
28 | val download_count: Int,
29 | val id: Int,
30 | val label: String,
31 | val name: String,
32 | val node_id: String,
33 | val size: Int,
34 | val state: String,
35 | val updated_at: String,
36 | val uploader: Uploader,
37 | val url: String
38 | )
39 |
40 | data class Author(
41 | val avatar_url: String,
42 | val events_url: String,
43 | val followers_url: String,
44 | val following_url: String,
45 | val gists_url: String,
46 | val gravatar_id: String,
47 | val html_url: String,
48 | val id: Int,
49 | val login: String,
50 | val node_id: String,
51 | val organizations_url: String,
52 | val received_events_url: String,
53 | val repos_url: String,
54 | val site_admin: Boolean,
55 | val starred_url: String,
56 | val subscriptions_url: String,
57 | val type: String,
58 | val url: String
59 | )
60 |
61 | data class Uploader(
62 | val avatar_url: String,
63 | val events_url: String,
64 | val followers_url: String,
65 | val following_url: String,
66 | val gists_url: String,
67 | val gravatar_id: String,
68 | val html_url: String,
69 | val id: Int,
70 | val login: String,
71 | val node_id: String,
72 | val organizations_url: String,
73 | val received_events_url: String,
74 | val repos_url: String,
75 | val site_admin: Boolean,
76 | val starred_url: String,
77 | val subscriptions_url: String,
78 | val type: String,
79 | val url: String
80 | )
81 | }
82 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/NetDTO.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data
2 |
3 | data class NetDTO(
4 | val fee: String? = null,
5 | val dynamicRemainFlow: String? = null,
6 | val dynamicUsedFlow: String? = null,
7 | val flag: String
8 | )
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/ReserveDTO.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data
2 |
3 | data class ReserveDTO(
4 | val data: ReserveInnerDTO? = null,
5 | val success: Boolean
6 | ) {
7 | data class ReserveInnerDTO(
8 | val addCode: String? = null,
9 | val area_name: String? = null,
10 | val attention_text: String? = null,
11 | val open_end: String? = null,
12 | val open_start: String? = null,
13 | val order_start: String? = null,
14 | val room_id: String? = null,
15 | val room_name: String? = null,
16 | val rule_cancel_time: String? = null,
17 | val rule_check_time: String? = null,
18 | val seat_id: String? = null,
19 | val seat_label: String? = null
20 | )
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/SessionDTO.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data
2 |
3 | data class SessionDTO(
4 | val message: String,
5 | val success: Boolean,
6 | val user_id: String
7 | )
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/VCardStatusDTO.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data
2 |
3 | data class VCardStatusDTO(
4 | val message: String,
5 | val resultData: ResultData,
6 | val success: Boolean
7 | ) {
8 |
9 | @Suppress("SpellCheckingInspection")
10 | data class ResultData(
11 | val message: String,
12 | val paytime: String,
13 | val status: String
14 | )
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/VolunteerDTO.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data
2 |
3 | data class VolunteerDTO(
4 | val numSameID: Int = 0,
5 | val numSameName: Int = 0,
6 | val totalDuration: Double = 0.0
7 | )
8 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/tools/Exam.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data.tools
2 |
3 | import com.qhy040404.datetime.Datetime
4 |
5 | data class Exam(
6 | val name: String,
7 | val time: String,
8 | val startTime: Datetime,
9 | val endTime: Datetime,
10 | val room: String
11 | )
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/tools/Grade.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data.tools
2 |
3 | data class Grade(
4 | val id: Int,
5 | val name: String,
6 | val code: String,
7 | val credit: Double,
8 | val grade: String,
9 | val gp: Double,
10 | val type: String
11 | )
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/tools/Lesson.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data.tools
2 |
3 | data class Lesson(
4 | val id: Int,
5 | val code: String,
6 | val compulsory: String,
7 | val name: String,
8 | val credit: Double,
9 | val teacher: String,
10 | val type: String
11 | )
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/data/tools/Semester.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.data.tools
2 |
3 | data class Semester(
4 | val id: Int,
5 | val name: String,
6 | val courses: List
7 | ) {
8 | var needsEvaluationTasks = mutableListOf()
9 | }
10 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/recyclerview/simplepage/Card.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.recyclerview.simplepage
2 |
3 | data class Card(
4 | val content: CharSequence,
5 | val lineSpacingExtra: Int = 0
6 | )
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/recyclerview/simplepage/CardView.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.recyclerview.simplepage
2 |
3 | import android.content.Context
4 | import android.util.TypedValue
5 | import android.widget.LinearLayout
6 | import androidx.appcompat.widget.AppCompatTextView
7 | import com.absinthe.libraries.utils.extensions.dp
8 | import com.qhy040404.libraryonetap.R
9 | import com.qhy040404.libraryonetap.utils.extensions.getColor
10 | import com.qhy040404.libraryonetap.utils.extensions.getDrawable
11 |
12 | class CardView(context: Context) : LinearLayout(context) {
13 | val content = AppCompatTextView(context).apply {
14 | layoutParams = LayoutParams(
15 | LayoutParams.WRAP_CONTENT,
16 | LayoutParams.WRAP_CONTENT
17 | )
18 | setPadding(paddingLeft, paddingTop, paddingRight, 8.dp)
19 | setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
20 | setTextColor(R.color.simple_page_card_text_color.getColor(context))
21 | setTextIsSelectable(true)
22 | }
23 |
24 | init {
25 | layoutParams = LayoutParams(
26 | LayoutParams.MATCH_PARENT,
27 | LayoutParams.WRAP_CONTENT
28 | )
29 | setPadding(16.dp, 16.dp, 12.dp, 16.dp)
30 | background = R.drawable.simplepage_card.getDrawable(context)
31 | elevation = 1.dp.toFloat()
32 |
33 | addView(content)
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/recyclerview/simplepage/CardViewBinder.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.recyclerview.simplepage
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import com.drakeet.multitype.ItemViewBinder
6 | import com.qhy040404.libraryonetap.base.BaseViewHolder
7 |
8 | class CardViewBinder : ItemViewBinder() {
9 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): BaseViewHolder {
10 | return BaseViewHolder(
11 | CardView(inflater.context)
12 | )
13 | }
14 |
15 | override fun onBindViewHolder(holder: BaseViewHolder, item: Card) {
16 | (holder.itemView as CardView).content.apply {
17 | setLineSpacing(
18 | item.lineSpacingExtra.toFloat(),
19 | (holder.itemView as CardView).content.lineSpacingMultiplier
20 | )
21 | text = item.content
22 | }
23 | }
24 |
25 | override fun getItemId(item: Card): Long {
26 | return item.hashCode().toLong()
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/recyclerview/simplepage/Category.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.recyclerview.simplepage
2 |
3 | data class Category(
4 | val title: String
5 | )
6 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/recyclerview/simplepage/CategoryView.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.recyclerview.simplepage
2 |
3 | import android.content.Context
4 | import android.view.Gravity
5 | import android.widget.LinearLayout
6 | import androidx.appcompat.widget.AppCompatTextView
7 | import androidx.core.view.marginBottom
8 | import androidx.core.view.marginTop
9 | import com.absinthe.libraries.utils.extensions.dp
10 | import com.qhy040404.libraryonetap.R
11 | import com.qhy040404.libraryonetap.utils.extensions.getColor
12 |
13 | class CategoryView(context: Context) : LinearLayout(context) {
14 | val title = AppCompatTextView(context).apply {
15 | layoutParams = LayoutParams(
16 | 0.dp,
17 | LayoutParams.WRAP_CONTENT
18 | ).apply {
19 | setMargins(1.dp, marginTop, 1.dp, marginBottom)
20 | weight = 1f
21 | }
22 | setPadding(
23 | 16.dp,
24 | 12.dp,
25 | 12.dp,
26 | 12.dp
27 | )
28 | setTextColor(R.color.simple_page_category_text_color.getColor(context))
29 | }
30 |
31 | init {
32 | layoutParams = LayoutParams(
33 | LayoutParams.MATCH_PARENT,
34 | LayoutParams.WRAP_CONTENT
35 | )
36 | gravity = Gravity.CENTER_VERTICAL
37 | orientation = HORIZONTAL
38 |
39 | addView(title)
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/recyclerview/simplepage/CategoryViewBinder.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.recyclerview.simplepage
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import com.drakeet.multitype.ItemViewBinder
6 | import com.qhy040404.libraryonetap.base.BaseViewHolder
7 |
8 | class CategoryViewBinder : ItemViewBinder() {
9 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): BaseViewHolder {
10 | return BaseViewHolder(
11 | CategoryView(inflater.context)
12 | )
13 | }
14 |
15 | override fun onBindViewHolder(holder: BaseViewHolder, item: Category) {
16 | (holder.itemView as CategoryView).title.text = item.title
17 | }
18 |
19 | override fun getItemId(item: Category): Long {
20 | return item.hashCode().toLong()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/recyclerview/simplepage/Clickable.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.recyclerview.simplepage
2 |
3 | data class Clickable(
4 | val name: String,
5 | val desc: String,
6 | val url: String? = null,
7 | val passed: Boolean = true
8 | )
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/recyclerview/simplepage/ClickableView.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.recyclerview.simplepage
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.view.View
6 | import android.widget.LinearLayout
7 | import androidx.appcompat.widget.AppCompatTextView
8 | import androidx.core.net.toUri
9 | import com.absinthe.libraries.utils.extensions.dp
10 | import com.qhy040404.libraryonetap.R
11 | import com.qhy040404.libraryonetap.utils.extensions.getColor
12 | import com.qhy040404.libraryonetap.utils.extensions.getDrawable
13 |
14 | class ClickableView(context: Context) : LinearLayout(context), View.OnClickListener {
15 | val name = AppCompatTextView(context).apply {
16 | layoutParams = LayoutParams(
17 | LayoutParams.WRAP_CONTENT,
18 | LayoutParams.WRAP_CONTENT
19 | )
20 | setTextColor(R.color.simple_page_name_color.getColor(context))
21 | }
22 |
23 | val desc = AppCompatTextView(context).apply {
24 | layoutParams = LayoutParams(
25 | LayoutParams.WRAP_CONTENT,
26 | LayoutParams.WRAP_CONTENT
27 | )
28 | setTextColor(R.color.simple_page_desc_color.getColor(context))
29 | }
30 |
31 | var url: String? = null
32 |
33 | init {
34 | layoutParams = LayoutParams(
35 | LayoutParams.MATCH_PARENT,
36 | LayoutParams.WRAP_CONTENT
37 | )
38 | orientation = VERTICAL
39 | setPadding(16.dp, 16.dp, 12.dp, 16.dp)
40 | background = R.drawable.simplepage_card.getDrawable(context)
41 | elevation = 1.dp.toFloat()
42 |
43 | addView(name)
44 | addView(desc)
45 | }
46 |
47 | override fun onClick(v: View) {
48 | url?.let {
49 | val intent = Intent(Intent.ACTION_VIEW)
50 | intent.data = it.toUri()
51 | runCatching {
52 | context.startActivity(intent)
53 | }
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/recyclerview/simplepage/ClickableViewBinder.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.recyclerview.simplepage
2 |
3 | import android.view.LayoutInflater
4 | import android.view.ViewGroup
5 | import com.drakeet.multitype.ItemViewBinder
6 | import com.qhy040404.libraryonetap.R
7 | import com.qhy040404.libraryonetap.base.BaseViewHolder
8 | import com.qhy040404.libraryonetap.utils.extensions.getColor
9 |
10 | class ClickableViewBinder : ItemViewBinder() {
11 | override fun onCreateViewHolder(inflater: LayoutInflater, parent: ViewGroup): BaseViewHolder {
12 | return BaseViewHolder(
13 | ClickableView(inflater.context)
14 | )
15 | }
16 |
17 | override fun onBindViewHolder(holder: BaseViewHolder, item: Clickable) {
18 | (holder.itemView as ClickableView).apply {
19 | name.text = item.name
20 | desc.text = item.desc
21 | url = item.url
22 | if (!item.passed) {
23 | name.setTextColor(R.color.red_500.getColor(context))
24 | }
25 | }
26 | }
27 |
28 | override fun getItemId(item: Clickable): Long {
29 | return item.hashCode().toLong()
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/recyclerview/simplepage/DividerItemDecoration.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.recyclerview.simplepage
2 |
3 | import android.graphics.Rect
4 | import android.view.View
5 | import androidx.recyclerview.widget.RecyclerView
6 | import androidx.recyclerview.widget.RecyclerView.ItemDecoration
7 | import com.drakeet.multitype.MultiTypeAdapter
8 |
9 | class DividerItemDecoration(private val adapter: MultiTypeAdapter) : ItemDecoration() {
10 | private val dividerClasses = arrayOf(Card::class.java, Clickable::class.java)
11 | override fun getItemOffsets(
12 | outRect: Rect,
13 | view: View,
14 | parent: RecyclerView,
15 | state: RecyclerView.State
16 | ) {
17 | if (adapter.itemCount == 0) {
18 | outRect.set(0, 0, 0, 0)
19 | return
20 | }
21 | val items: List<*> = adapter.items
22 | val position = parent.getChildAdapterPosition(view)
23 | val should = position + 1 < items.size &&
24 | dividerClasses.contains(items[position]!!.javaClass) &&
25 | dividerClasses.contains(items[position + 1]!!.javaClass)
26 | if (should) {
27 | outRect.set(0, 0, 0, 1)
28 | } else {
29 | outRect.set(0, 0, 0, 0)
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/ui/about/ClickableViewHolder.java:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.ui.about;
2 |
3 | import android.content.ActivityNotFoundException;
4 | import android.content.Intent;
5 | import android.net.Uri;
6 | import android.view.View;
7 |
8 | import androidx.annotation.Nullable;
9 | import androidx.browser.customtabs.CustomTabsIntent;
10 | import androidx.recyclerview.widget.RecyclerView;
11 |
12 | /**
13 | * @author drakeet
14 | * @author qhy040404
15 | */
16 | public class ClickableViewHolder extends RecyclerView.ViewHolder {
17 | private @Nullable
18 | String url;
19 |
20 | public ClickableViewHolder(View itemView) {
21 | super(itemView);
22 | itemView.setOnClickListener(v -> {
23 | if (url != null) {
24 | try {
25 | new CustomTabsIntent.Builder().build()
26 | .launchUrl(v.getContext(), Uri.parse(url));
27 | } catch (ActivityNotFoundException ignored1) {
28 | Intent intent = new Intent(Intent.ACTION_VIEW);
29 | intent.setData(Uri.parse(url));
30 | try {
31 | v.getContext().startActivity(intent);
32 | } catch (ActivityNotFoundException ignored2) {
33 | }
34 | }
35 | }
36 | });
37 | }
38 |
39 | public void setURL(@Nullable String url) {
40 | this.url = url;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/ui/about/ContributorViewBinder.java:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.ui.about;
2 |
3 | import static android.net.Uri.parse;
4 |
5 | import android.content.ActivityNotFoundException;
6 | import android.content.Intent;
7 | import android.view.LayoutInflater;
8 | import android.view.View;
9 | import android.view.ViewGroup;
10 | import android.widget.ImageView;
11 | import android.widget.TextView;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.browser.customtabs.CustomTabsIntent;
15 | import androidx.recyclerview.widget.RecyclerView;
16 |
17 | import com.drakeet.about.Contributor;
18 | import com.drakeet.about.OnContributorClickedListener;
19 | import com.drakeet.about.R;
20 | import com.drakeet.multitype.ItemViewBinder;
21 |
22 | /**
23 | * @author drakeet
24 | * @author qhy040404
25 | */
26 | public class ContributorViewBinder extends ItemViewBinder {
27 | private @NonNull
28 | final AbsAboutActivityProxy activity;
29 |
30 | public ContributorViewBinder(@NonNull AbsAboutActivityProxy activity) {
31 | this.activity = activity;
32 | }
33 |
34 | @Override
35 | @NonNull
36 | public CBViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
37 | return new CBViewHolder(inflater.inflate(R.layout.about_page_item_contributor, parent, false), activity);
38 | }
39 |
40 | @Override
41 | public void onBindViewHolder(@NonNull CBViewHolder holder, @NonNull Contributor contributor) {
42 | holder.avatar.setImageResource(contributor.avatarResId);
43 | holder.name.setText(contributor.name);
44 | holder.desc.setText(contributor.desc);
45 | holder.data = contributor;
46 | }
47 |
48 | @Override
49 | public long getItemId(@NonNull Contributor item) {
50 | return item.hashCode();
51 | }
52 |
53 | public static class CBViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
54 | public final ImageView avatar;
55 | public final TextView name;
56 | public final TextView desc;
57 |
58 | protected @NonNull
59 | final AbsAboutActivityProxy activity;
60 |
61 | public Contributor data;
62 |
63 | public CBViewHolder(View itemView, @NonNull AbsAboutActivityProxy activity) {
64 | super(itemView);
65 | this.activity = activity;
66 | avatar = itemView.findViewById(R.id.avatar);
67 | name = itemView.findViewById(R.id.name);
68 | desc = itemView.findViewById(R.id.desc);
69 | itemView.setOnClickListener(this);
70 | }
71 |
72 | @Override
73 | public void onClick(View v) {
74 | OnContributorClickedListener listener = activity.getOnContributorClickedListener();
75 | if (listener != null && listener.onContributorClicked(v, data)) {
76 | return;
77 | }
78 | if (data.url != null) {
79 | try {
80 | new CustomTabsIntent.Builder().build()
81 | .launchUrl(v.getContext(), parse(data.url));
82 | } catch (ActivityNotFoundException ignored1) {
83 | Intent intent = new Intent(Intent.ACTION_VIEW);
84 | intent.setData(parse(data.url));
85 | try {
86 | v.getContext().startActivity(intent);
87 | } catch (ActivityNotFoundException ignored2) {
88 | }
89 | }
90 | }
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/ui/about/DividerItemDecoration.java:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.ui.about;
2 |
3 | import android.graphics.Rect;
4 | import android.view.View;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.recyclerview.widget.LinearLayoutManager;
8 | import androidx.recyclerview.widget.RecyclerView;
9 |
10 | import com.drakeet.about.License;
11 | import com.drakeet.about.Recommendation;
12 | import com.drakeet.multitype.MultiTypeAdapter;
13 |
14 | import java.util.List;
15 |
16 | /**
17 | * @author drakeet
18 | * @author qhy040404
19 | */
20 | class DividerItemDecoration extends RecyclerView.ItemDecoration {
21 | private final @NonNull
22 | MultiTypeAdapter adapter;
23 |
24 | private final Class>[] dividerClasses = {License.class, Recommendation.class};
25 |
26 | /**
27 | * Creates a divider {@link RecyclerView.ItemDecoration} that can be used with a
28 | * {@link LinearLayoutManager}.
29 | *
30 | * @param adapter The MultiTypeAdapter
31 | */
32 | DividerItemDecoration(@NonNull MultiTypeAdapter adapter) {
33 | this.adapter = adapter;
34 | }
35 |
36 | @Override
37 | public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
38 | if (adapter.getItemCount() == 0) {
39 | outRect.set(0, 0, 0, 0);
40 | return;
41 | }
42 | List> items = adapter.getItems();
43 | int position = parent.getChildAdapterPosition(view);
44 | boolean should = false;
45 | for (int i = 0; !should && i < dividerClasses.length; i++) {
46 | should = position + 1 < items.size()
47 | && items.get(position).getClass().isAssignableFrom(dividerClasses[i])
48 | && items.get(position + 1).getClass().isAssignableFrom(dividerClasses[i]);
49 | }
50 | if (should) {
51 | outRect.set(0, 0, 0, 1);
52 | } else {
53 | outRect.set(0, 0, 0, 0);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/ui/about/LicenseViewBinder.java:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.ui.about;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.view.LayoutInflater;
5 | import android.view.View;
6 | import android.view.ViewGroup;
7 | import android.widget.TextView;
8 |
9 | import androidx.annotation.NonNull;
10 |
11 | import com.drakeet.about.License;
12 | import com.drakeet.about.R;
13 | import com.drakeet.multitype.ItemViewBinder;
14 |
15 | /**
16 | * @author drakeet
17 | * @author qhy040404
18 | */
19 | public class LicenseViewBinder extends ItemViewBinder {
20 | @Override
21 | @NonNull
22 | public LViewHolder onCreateViewHolder(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent) {
23 | return new LViewHolder(inflater.inflate(R.layout.about_page_item_license, parent, false));
24 | }
25 |
26 | @Override
27 | @SuppressLint("SetTextI18n")
28 | public void onBindViewHolder(@NonNull LViewHolder holder, @NonNull License data) {
29 | holder.content.setText(data.name + " - " + data.author);
30 | holder.hint.setText(data.url + "\n" + data.type);
31 | holder.setURL(data.url);
32 | }
33 |
34 | @Override
35 | public long getItemId(@NonNull License item) {
36 | return item.hashCode();
37 | }
38 |
39 | public static class LViewHolder extends ClickableViewHolder {
40 | public final TextView content;
41 | public final TextView hint;
42 |
43 | public LViewHolder(View itemView) {
44 | super(itemView);
45 | content = itemView.findViewById(R.id.content);
46 | hint = itemView.findViewById(R.id.hint);
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/ui/fragment/fullscreen/FullScreenDialogFragment.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.ui.fragment.fullscreen
2 |
3 | import android.graphics.Color
4 | import android.graphics.drawable.ColorDrawable
5 | import android.os.Bundle
6 | import android.view.Gravity
7 | import android.view.LayoutInflater
8 | import android.view.View
9 | import android.view.ViewGroup
10 | import androidx.fragment.app.DialogFragment
11 | import com.qhy040404.libraryonetap.LibraryOneTapApp
12 | import com.qhy040404.libraryonetap.view.FullScreenDialogView
13 |
14 | class FullScreenDialogFragment(private val message: String) : DialogFragment() {
15 | override fun onCreate(savedInstanceState: Bundle?) {
16 | super.onCreate(savedInstanceState)
17 | LibraryOneTapApp.instance?.addFragment(this)
18 | }
19 |
20 | override fun onCreateView(
21 | inflater: LayoutInflater,
22 | container: ViewGroup?,
23 | savedInstanceState: Bundle?
24 | ): View {
25 | dialog?.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
26 | return FullScreenDialogView(requireContext()).apply {
27 | detail.text = message
28 | }
29 | }
30 |
31 | override fun onResume() {
32 | super.onResume()
33 | dialog?.apply {
34 | run {
35 | setCanceledOnTouchOutside(isCancelable)
36 | setCancelable(isCancelable)
37 | }
38 | window?.run {
39 | attributes = attributes?.apply {
40 | width = ViewGroup.LayoutParams.MATCH_PARENT
41 | height = ViewGroup.LayoutParams.MATCH_PARENT
42 | gravity = Gravity.BOTTOM
43 | }
44 | }
45 | }
46 | }
47 |
48 | override fun isCancelable() = false
49 | }
50 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/ui/interfaces/IAppBarContainer.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.ui.interfaces
2 |
3 | import android.view.View
4 |
5 | interface IAppBarContainer {
6 | fun scheduleAppbarLiftingStatus(isLifted: Boolean)
7 | fun setLiftOnScrollTargetView(targetView: View)
8 | }
9 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/ui/tools/BaseEduActivity.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.ui.tools
2 |
3 | import androidx.annotation.StringRes
4 | import com.google.android.material.dialog.MaterialAlertDialogBuilder
5 | import com.qhy040404.libraryonetap.R
6 | import com.qhy040404.libraryonetap.constant.GlobalValues
7 | import com.qhy040404.libraryonetap.constant.URLManager
8 | import com.qhy040404.libraryonetap.recyclerview.SimplePageActivity
9 | import com.qhy040404.libraryonetap.utils.web.Requests
10 |
11 | abstract class BaseEduActivity : SimplePageActivity() {
12 | protected var currentVisible = true
13 |
14 | fun showInitFailedAlertDialog(@StringRes title: Int) {
15 | MaterialAlertDialogBuilder(this)
16 | .setTitle(title)
17 | .setMessage(GlobalValues.ssoPrompt)
18 | .setPositiveButton(R.string.glb_ok) { _, _ ->
19 | finish()
20 | }
21 | .setCancelable(false)
22 | .create().also {
23 | if (this.currentVisible) {
24 | it.show()
25 | }
26 | }
27 | }
28 |
29 | fun initMinor() {
30 | if (GlobalValues.majorStuId == 0 || GlobalValues.minorStuId == 0) {
31 | val initUrl = Requests.get(URLManager.EDU_GRADE_INIT_URL, getUrl = true)
32 | val initData = Requests.get(URLManager.EDU_GRADE_INIT_URL)
33 |
34 | if (initUrl.contains("semester-index")) {
35 | GlobalValues.majorStuId = initUrl.substringAfter("semester-index/").toInt()
36 | GlobalValues.minorStuId = -1
37 | } else {
38 | val initList = initData.split("onclick=\"myFunction(this)\" value=\"")
39 | if (initList.size == 3) {
40 | val aStuId = initList[1].substringBefore("\"").toInt()
41 | val bStuId = initList[2].substringBefore("\"").toInt()
42 | GlobalValues.majorStuId = aStuId.coerceAtMost(bStuId)
43 | GlobalValues.minorStuId = aStuId.coerceAtLeast(bStuId)
44 | }
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/AppUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import android.content.Context
4 | import android.content.res.Configuration
5 | import androidx.annotation.StringRes
6 | import androidx.appcompat.app.AppCompatDelegate
7 | import com.google.android.material.dialog.MaterialAlertDialogBuilder
8 | import com.qhy040404.libraryonetap.R
9 | import com.qhy040404.libraryonetap.constant.Constants
10 |
11 | object AppUtils {
12 | fun getNightMode(modeString: String) = when (modeString) {
13 | "on" -> AppCompatDelegate.MODE_NIGHT_YES
14 | "off" -> AppCompatDelegate.MODE_NIGHT_NO
15 | else -> AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM
16 | }
17 |
18 | fun getThemeID(theme: String): Int {
19 | return when (theme) {
20 | "purple" -> R.style.Theme_Purple_NoActionBar
21 | "library" -> R.style.Theme_Main_NoActionBar
22 | "blue" -> R.style.Theme_Blue_NoActionBar
23 | "pink" -> R.style.Theme_Pink_NoActionBar
24 | "green" -> R.style.Theme_Green_NoActionBar
25 | "orange" -> R.style.Theme_Orange_NoActionBar
26 | "red" -> R.style.Theme_Red_NoActionBar
27 | "simple" -> R.style.Theme_Simple_NoActionBar
28 | else -> getThemeID(RandomDataUtils.randomTheme)
29 | }
30 | }
31 |
32 | fun checkData(
33 | id: String,
34 | passwd: String,
35 | context: Context? = null,
36 | @StringRes titleId: Int? = null,
37 | @StringRes messageId: Int? = null
38 | ): Boolean {
39 | (id != "Error" && passwd != "Error" && id.isNotEmpty() && passwd.isNotEmpty()).let {
40 | if (!it && context != null && titleId != null && messageId != null) {
41 | MaterialAlertDialogBuilder(context)
42 | .setTitle(titleId)
43 | .setMessage(messageId)
44 | .setPositiveButton(R.string.glb_ok, null)
45 | .setCancelable(true)
46 | .create()
47 | .show()
48 | }
49 | return it
50 | }
51 | }
52 |
53 | fun currentIsNightMode(context: Context): Boolean {
54 | val uiMode = context.resources.configuration.uiMode
55 | return when (uiMode and Configuration.UI_MODE_NIGHT_MASK) {
56 | Configuration.UI_MODE_NIGHT_YES -> true
57 | else -> false
58 | }
59 | }
60 |
61 | fun hasNetwork(): Boolean {
62 | return NetworkStateUtils.checkNetworkType() != Constants.GLOBAL_ERROR
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/CacheUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import com.qhy040404.libraryonetap.LibraryOneTapApp
4 | import java.io.File
5 | import java.text.DecimalFormat
6 |
7 | object CacheUtils {
8 | private val externalCache = LibraryOneTapApp.app.externalCacheDir
9 | private val cache = LibraryOneTapApp.app.cacheDir
10 | private val codeCache = LibraryOneTapApp.app.codeCacheDir
11 | private val df = DecimalFormat("#.00")
12 |
13 | fun getCacheSize() = formatFileSize(
14 | FileUtils.getFolderSize(externalCache!!) +
15 | FileUtils.getFolderSize(cache) +
16 | FileUtils.getFolderSize(codeCache)
17 | )
18 |
19 | private fun formatFileSize(fileSize: Long) = if (fileSize == 0L) {
20 | "0.00 K"
21 | } else if (fileSize < 1024) {
22 | df.format(fileSize.toDouble()) + " B"
23 | } else if (fileSize < 1024 * 1024) {
24 | df.format(fileSize.toDouble() / 1024) + " K"
25 | } else if (fileSize < 1024 * 1024 * 1024) {
26 | df.format(fileSize.toDouble() / (1024 * 1024)) + " M"
27 | } else {
28 | df.format(fileSize.toDouble() / (1024 * 1024 * 1024)) + " G"
29 | }
30 |
31 | fun trimCaches() {
32 | trimCache(cache)
33 | trimCache(codeCache)
34 | trimCache(externalCache)
35 | }
36 |
37 | private fun trimCache(file: File?) = runCatching {
38 | if (file != null && file.isDirectory && file.listFiles()!!.isNotEmpty()) {
39 | FileUtils.delete(file)
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/FileUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import java.io.File
4 |
5 | object FileUtils {
6 | fun delete(file: File?): Boolean {
7 | if (file == null) return false
8 | return if (file.isDirectory) {
9 | deleteDir(file)
10 | } else {
11 | deleteFile(file)
12 | }
13 | }
14 |
15 | private fun deleteDir(dir: File?): Boolean {
16 | if (dir == null) return false
17 | if (!dir.exists()) return true
18 | if (!dir.isDirectory) return false
19 | val files = dir.listFiles()
20 | if (files != null && files.isNotEmpty()) {
21 | for (file in files) {
22 | if (file.isFile) {
23 | if (!file.delete()) return false
24 | } else if (file.isDirectory) {
25 | if (!deleteDir(file)) return false
26 | }
27 | }
28 | }
29 | return dir.delete()
30 | }
31 |
32 | private fun deleteFile(file: File?): Boolean {
33 | return file != null && (!file.exists() || file.isFile && file.delete())
34 | }
35 |
36 | fun getFolderSize(file: File): Long {
37 | var size = 0L
38 | runCatching {
39 | val fileList = file.listFiles()
40 | if (fileList != null) {
41 | for (i in fileList.indices) {
42 | size += if (fileList[i].isDirectory) {
43 | getFolderSize(fileList[i])
44 | } else {
45 | fileList[i].length()
46 | }
47 | }
48 | }
49 | }
50 | return size
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/HashUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import com.qhy040404.libraryonetap.utils.extensions.toHex
4 | import java.security.MessageDigest
5 |
6 | object HashUtils {
7 | fun md5(byteArray: ByteArray): String = digest(byteArray, "MD5")
8 |
9 | fun sha1(byteArray: ByteArray): String = digest(byteArray, "SHA-1")
10 |
11 | fun sha256(byteArray: ByteArray): String = digest(byteArray, "SHA-256")
12 |
13 | fun sha512(byteArray: ByteArray): String = digest(byteArray, "SHA-512")
14 |
15 | private fun digest(byteArray: ByteArray, algorithm: String): String {
16 | val messageDigest = MessageDigest.getInstance(algorithm).apply {
17 | reset()
18 | }
19 | messageDigest.update(byteArray)
20 | return messageDigest.digest().toHex()
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/JsonUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import com.qhy040404.libraryonetap.constant.GlobalManager.moshi
4 | import org.intellij.lang.annotations.Language
5 |
6 | object JsonUtils {
7 | @Language("JSON")
8 | inline fun toJson(value: T?): String? = runCatching {
9 | moshi.adapter(T::class.java).toJson(value)
10 | }.getOrNull()
11 | }
12 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/MarkdownUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import org.intellij.markdown.flavours.commonmark.CommonMarkFlavourDescriptor
4 | import org.intellij.markdown.html.HtmlGenerator
5 | import org.intellij.markdown.parser.MarkdownParser
6 |
7 | object MarkdownUtils {
8 | /**
9 | * Parse markdown to HTML
10 | *
11 | * @param src markdown
12 | * @return parsed HTML string
13 | */
14 | fun fromString(src: String): String {
15 | val flavour = CommonMarkFlavourDescriptor()
16 | val parsedTree = MarkdownParser(flavour).buildMarkdownTreeFromString(src)
17 | return HtmlGenerator(src, parsedTree, flavour).generateHtml()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/NetworkStateUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import android.net.ConnectivityManager
4 | import android.net.NetworkCapabilities
5 | import androidx.core.content.getSystemService
6 | import com.qhy040404.libraryonetap.LibraryOneTapApp
7 | import com.qhy040404.libraryonetap.constant.Constants
8 |
9 | object NetworkStateUtils {
10 | fun checkNetworkType(): String {
11 | val manager = LibraryOneTapApp.app.getSystemService()
12 | ?: return Constants.GLOBAL_ERROR
13 | val networkCapabilities =
14 | manager.getNetworkCapabilities(manager.activeNetwork) ?: return Constants.GLOBAL_ERROR
15 | return when {
16 | networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> "WIFI"
17 | networkCapabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> "Cellular"
18 | else -> Constants.GLOBAL_ERROR
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/OsUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import android.os.Build
4 | import androidx.annotation.ChecksSdkIntAtLeast
5 |
6 | object OsUtils {
7 | @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.TIRAMISU)
8 | fun atLeastT(): Boolean {
9 | return Build.VERSION.SDK_INT >= 33
10 | }
11 |
12 | @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.S)
13 | fun atLeastS(): Boolean {
14 | return Build.VERSION.SDK_INT >= 31
15 | }
16 |
17 | @ChecksSdkIntAtLeast(api = Build.VERSION_CODES.R)
18 | fun atLeastR(): Boolean {
19 | return Build.VERSION.SDK_INT >= 30
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/PackageUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import android.content.pm.ApplicationInfo
4 | import android.content.pm.PackageInfo
5 | import android.content.pm.PackageManager
6 | import com.qhy040404.libraryonetap.LibraryOneTapApp
7 | import com.qhy040404.libraryonetap.compat.PMCompat
8 |
9 | object PackageUtils {
10 | private val packInfo: PackageInfo =
11 | PMCompat.getPackageInfo(LibraryOneTapApp.app.packageName, 0)
12 | private val appInfo: ApplicationInfo = PMCompat.getApplicationInfo(
13 | LibraryOneTapApp.app.packageName,
14 | PackageManager.GET_META_DATA
15 | )
16 |
17 | val versionCode = packInfo.longVersionCode
18 | val versionName: String = packInfo.versionName
19 |
20 | val buildType = appInfo.metaData.getString("Channel").toString()
21 | }
22 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/PermissionUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import android.app.Activity
4 | import android.content.pm.PackageManager
5 | import androidx.annotation.StringRes
6 | import androidx.fragment.app.FragmentManager
7 | import com.qhy040404.libraryonetap.ui.fragment.fullscreen.FullScreenDialogFragment
8 | import com.qhy040404.libraryonetap.utils.extensions.getString
9 |
10 | object PermissionUtils {
11 | fun checkPermission(
12 | activity: Activity,
13 | permission: String,
14 | childFragmentMgr: FragmentManager,
15 | @StringRes strId: Int
16 | ): Boolean {
17 | return if (activity.checkSelfPermission(permission) == PackageManager.PERMISSION_DENIED) {
18 | FullScreenDialogFragment(strId.getString()).apply {
19 | show(childFragmentMgr, null)
20 | }
21 | activity.requestPermissions(arrayOf(permission), 100)
22 | false
23 | } else {
24 | true
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/QRUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import android.graphics.Bitmap
4 | import android.graphics.Canvas
5 | import android.graphics.Color
6 | import android.graphics.Paint
7 | import android.graphics.RectF
8 |
9 | object QRUtils {
10 | fun toGrayscale(bmpOriginal: Bitmap): Bitmap {
11 | val width = bmpOriginal.width
12 | val height = bmpOriginal.height
13 | var color: Int
14 | var r: Int
15 | var g: Int
16 | var b: Int
17 | var a: Int
18 |
19 | val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
20 |
21 | val oldPx = IntArray(width * height)
22 | val newPx = IntArray(width * height)
23 | bmpOriginal.getPixels(oldPx, 0, width, 0, 0, width, height)
24 |
25 | for (j in 0 until width * height) {
26 | color = oldPx[j]
27 | r = Color.red(color)
28 | g = Color.green(color)
29 | b = Color.blue(color)
30 | a = Color.alpha(color)
31 | var gray = (r.toFloat() * 0.3 + g.toFloat() * 0.59 + b.toFloat() * 0.11).toInt()
32 | gray = if (gray < 128) {
33 | 0
34 | } else {
35 | 255
36 | }
37 | newPx[j] = Color.argb(a, gray, gray, gray)
38 | }
39 |
40 | bmp.setPixels(newPx, 0, width, 0, 0, width, height)
41 | return createWhiteBorderBitmap(bmp)
42 | }
43 |
44 | fun createWhiteBorderBitmap(bmp: Bitmap, width: Int = 10, radius: Float = 5F): Bitmap {
45 | val outBmp = Bitmap.createBitmap(
46 | bmp.width + width * 2,
47 | bmp.height + width * 2,
48 | Bitmap.Config.ARGB_8888
49 | )
50 | val canvas = Canvas(outBmp)
51 |
52 | val rectF = RectF(0F, 0F, outBmp.width.toFloat(), outBmp.height.toFloat())
53 |
54 | val paint = Paint()
55 | paint.isAntiAlias = true
56 | paint.color = Color.WHITE
57 |
58 | canvas.drawRoundRect(rectF, radius, radius, paint)
59 | canvas.drawBitmap(bmp, width.toFloat(), width.toFloat(), paint)
60 |
61 | return outBmp
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/RandomDataUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import com.qhy040404.libraryonetap.constant.GlobalManager
4 | import com.qhy040404.libraryonetap.utils.lazy.resettableLazy
5 | import java.util.Random
6 |
7 | object RandomDataUtils {
8 | fun getNum(numCount: Int) = if (numCount > 0) {
9 | Random().nextInt(numCount)
10 | } else {
11 | 0
12 | }
13 |
14 | val randomTheme by resettableLazy(GlobalManager.lazyMgr) {
15 | when (getNum(8)) {
16 | 0 -> "purple"
17 | 1 -> "library"
18 | 2 -> "blue"
19 | 3 -> "pink"
20 | 4 -> "green"
21 | 5 -> "orange"
22 | 6 -> "red"
23 | else -> "simple"
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/SPDelegates.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import kotlin.properties.ReadWriteProperty
4 | import kotlin.reflect.KProperty
5 |
6 | class SPDelegates(private val key: String, private val default: T) : ReadWriteProperty {
7 | override fun getValue(thisRef: Any?, property: KProperty<*>) =
8 | SPUtils.getValue(key, default)
9 |
10 | override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) =
11 | SPUtils.setValue(key, value)
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/SPUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import android.content.Context
4 | import android.content.SharedPreferences
5 | import com.qhy040404.libraryonetap.LibraryOneTapApp
6 | import com.qhy040404.libraryonetap.constant.GlobalValues.SP_NAME
7 | import com.qhy040404.libraryonetap.utils.lazy.resettableLazy
8 | import com.qhy040404.libraryonetap.utils.lazy.resettableManager
9 |
10 | object SPUtils {
11 | val spLazyMgr = resettableManager()
12 | val sp: SharedPreferences by resettableLazy(spLazyMgr) {
13 | LibraryOneTapApp.app.getSharedPreferences(
14 | SP_NAME,
15 | Context.MODE_PRIVATE
16 | )
17 | }
18 |
19 | fun getValue(name: String, default: T, confirm: Boolean = false): T {
20 | val value = with(sp) {
21 | val res: Any = when (default) {
22 | is Long -> getLong(name, default)
23 | is String -> getString(name, default).orEmpty()
24 | is Int -> getInt(name, default)
25 | is Boolean -> getBoolean(name, default)
26 | is Float -> getFloat(name, default)
27 | else -> throw IllegalArgumentException("Unknown data type")
28 | }
29 | @Suppress("UNCHECKED_CAST")
30 | res as T
31 | }
32 | if (!confirm && value == default) {
33 | spLazyMgr.reset()
34 | return getValue(name, default, true)
35 | }
36 | return value
37 | }
38 |
39 | fun setValue(name: String, value: T) = with(sp.edit()) {
40 | when (value) {
41 | is Long -> putLong(name, value)
42 | is String -> putString(name, value)
43 | is Int -> putInt(name, value)
44 | is Boolean -> putBoolean(name, value)
45 | is Float -> putFloat(name, value)
46 | else -> throw IllegalArgumentException("This type can't be saved into Preferences")
47 | }.apply()
48 | }
49 |
50 | fun resetAll() = with(sp.edit()) {
51 | clear().apply()
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/TimeUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import com.qhy040404.datetime.Datetime
4 | import com.qhy040404.libraryonetap.utils.extensions.one2two
5 |
6 | object TimeUtils {
7 | fun getToday(separator: String, doubleNum: Boolean): String {
8 | val now = Datetime.now()
9 | val year = now.year.toString()
10 | val month = if (doubleNum) {
11 | now.month.one2two()
12 | } else {
13 | now.month.toString()
14 | }
15 | val day = if (doubleNum) {
16 | now.day.one2two()
17 | } else {
18 | now.day.toString()
19 | }
20 | return "$year$separator$month$separator$day"
21 | }
22 |
23 | fun isValidReserveTime(): Boolean {
24 | return Datetime.now().let {
25 | it.hour in 7..22 || it.hour == 6 && it.minute > 30
26 | }
27 | }
28 |
29 | fun isServerAvailableTime(): Boolean {
30 | return Datetime.now().hour in 6 until 23
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/Toasty.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils
2 |
3 | import android.content.Context
4 | import android.os.Handler
5 | import android.os.Looper
6 | import android.view.ContextThemeWrapper
7 | import android.view.Gravity
8 | import android.widget.Toast
9 | import androidx.annotation.AnyThread
10 | import androidx.annotation.MainThread
11 | import androidx.annotation.StringRes
12 | import com.qhy040404.libraryonetap.view.ToastView
13 | import java.lang.ref.WeakReference
14 |
15 | object Toasty {
16 | private val handler = Handler(Looper.getMainLooper())
17 | private var toastRef: WeakReference? = null
18 |
19 | fun cancel() = toastRef?.get()?.cancel()
20 |
21 | @AnyThread
22 | fun showShort(context: Context, message: String) {
23 | if (Looper.getMainLooper() == Looper.myLooper()) {
24 | //noinspection WrongThread
25 | show(context, message, Toast.LENGTH_SHORT)
26 | } else {
27 | handler.post { show(context, message, Toast.LENGTH_SHORT) }
28 | }
29 | }
30 |
31 | @AnyThread
32 | fun showShort(context: Context, @StringRes res: Int) =
33 | showShort(context, context.getString(res))
34 |
35 | @AnyThread
36 | fun showLong(context: Context, message: String) {
37 | if (Looper.getMainLooper() == Looper.myLooper()) {
38 | //noinspection WrongThread
39 | show(context, message, Toast.LENGTH_LONG)
40 | } else {
41 | handler.post { show(context, message, Toast.LENGTH_LONG) }
42 | }
43 | }
44 |
45 | @AnyThread
46 | fun showLong(context: Context, @StringRes res: Int) =
47 | showLong(context, context.getString(res))
48 |
49 | @Suppress("DEPRECATION")
50 | @MainThread
51 | private fun show(context: Context, message: String, duration: Int) {
52 | cancel()
53 |
54 | val toast = if (OsUtils.atLeastR() && context !is ContextThemeWrapper) {
55 | Toast(context).also {
56 | it.duration = duration
57 | it.setText(message)
58 | }
59 | } else {
60 | val view = ToastView(context).also {
61 | it.message.text = message
62 | }
63 | Toast(context).also {
64 | it.setGravity(Gravity.CENTER_HORIZONTAL or Gravity.BOTTOM, 0, 200)
65 | it.duration = duration
66 | it.view = view
67 | }
68 | }
69 | toastRef = WeakReference(toast)
70 | toast.show()
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/encrypt/AesEncryptUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.encrypt
2 |
3 | import android.annotation.SuppressLint
4 | import android.util.Base64
5 | import com.qhy040404.libraryonetap.utils.extensions.toHex
6 | import java.security.Security
7 | import javax.crypto.Cipher
8 | import javax.crypto.spec.IvParameterSpec
9 | import javax.crypto.spec.SecretKeySpec
10 | import org.spongycastle.jce.provider.BouncyCastleProvider
11 |
12 | object AesEncryptUtils {
13 | private const val cipherMode = "AES/CFB/NoPadding"
14 |
15 | private fun encrypt(text: String, key: String, iv: String): String {
16 | val sKeySpec = SecretKeySpec(key.toByteArray(), "AES")
17 | val cipher = Cipher.getInstance(cipherMode)
18 | cipher.init(Cipher.ENCRYPT_MODE, sKeySpec, IvParameterSpec(iv.toByteArray()))
19 | return cipher.doFinal(text.toByteArray()).toHex()
20 | }
21 |
22 | fun vpnEncrypt(text: String, key: String, iv: String): String {
23 | return iv.toByteArray().toHex() + encrypt(text, key, iv)
24 | }
25 |
26 | @SuppressLint("GetInstance")
27 | object VCard {
28 | private const val vCardCipherMode = "AES/ECB/PKCS7Padding"
29 |
30 | init {
31 | Security.addProvider(BouncyCastleProvider())
32 | }
33 |
34 | fun genKey(template: Int = 16): String {
35 | val map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
36 | return buildString {
37 | repeat(template) {
38 | append(map[(Math.random() * map.length).toInt()])
39 | }
40 | }
41 | }
42 |
43 | fun encrypt(data: String, key: String): String {
44 | return Cipher.getInstance(vCardCipherMode, "SC").apply {
45 | init(Cipher.ENCRYPT_MODE, SecretKeySpec(key.toByteArray(Charsets.UTF_8), "AES"))
46 | }.let { cipher ->
47 | Base64.encodeToString(
48 | cipher.doFinal(data.toByteArray(Charsets.UTF_8)),
49 | Base64.DEFAULT
50 | )
51 | }
52 | }
53 |
54 | fun decrypt(data: String, key: String): String {
55 | return Cipher.getInstance(vCardCipherMode, "SC").apply {
56 | init(Cipher.DECRYPT_MODE, SecretKeySpec(key.toByteArray(Charsets.UTF_8), "AES"))
57 | }.doFinal(Base64.decode(data, Base64.DEFAULT)).decodeToString()
58 | }
59 |
60 | fun handleKey(data: String, reversed: Boolean): String {
61 | return if (reversed) {
62 | (data.substring(10) + data.substring(0, 10)).reversed()
63 | } else {
64 | data.reversed().let {
65 | it.substring(6) + it.substring(0, 6)
66 | }
67 | }
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/extensions/AnyExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.extensions
2 |
3 | import com.qhy040404.libraryonetap.utils.JsonUtils
4 |
5 | /**
6 | * Convert any data class to json string
7 | *
8 | * @return JSON String
9 | */
10 | fun Any?.toJson(): String? = JsonUtils.toJson(this)
11 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/extensions/ByteExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.extensions
2 |
3 | import com.qhy040404.libraryonetap.utils.HashUtils
4 |
5 | /**
6 | * Calculate a ByteArray's MD5 hash
7 | *
8 | * @return MD5 hash of the ByteArray
9 | */
10 | fun ByteArray.md5() = HashUtils.md5(this)
11 |
12 | /**
13 | * Calculate a ByteArray's SHA-1 hash
14 | *
15 | * @return SHA-1 hash of the ByteArray
16 | */
17 | fun ByteArray.sha1() = HashUtils.sha1(this)
18 |
19 | /**
20 | * Calculate a ByteArray's SHA-256 hash
21 | *
22 | * @return SHA-256 hash of the ByteArray
23 | */
24 | fun ByteArray.sha256() = HashUtils.sha256(this)
25 |
26 | /**
27 | * Calculate a ByteArray's SHA-512 hash
28 | *
29 | * @return SHA-512 hash of the ByteArray
30 | */
31 | fun ByteArray.sha512() = HashUtils.sha512(this)
32 |
33 | /**
34 | * Convert a ByteArray to Hex String
35 | *
36 | * @return HexString of the ByteArray
37 | */
38 | fun ByteArray.toHex(): String {
39 | return buildString {
40 | this@toHex.forEach {
41 | var hex = Integer.toHexString(it.toInt() and 0xFF)
42 | if (hex.length == 1) {
43 | hex = "0$hex"
44 | }
45 | append(hex.lowercase())
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/extensions/ContextExtension.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.extensions
2 |
3 | import android.content.Context
4 | import androidx.annotation.AnyThread
5 | import androidx.annotation.StringRes
6 | import com.qhy040404.libraryonetap.utils.Toasty
7 |
8 | /**
9 | * Show short-time toast with String
10 | *
11 | * @param message original string
12 | */
13 | @AnyThread
14 | fun Context.showToast(message: String) = Toasty.showShort(this, message)
15 |
16 | /**
17 | * Show short-time toast with String
18 | *
19 | * @param res string's resource id
20 | */
21 | @AnyThread
22 | fun Context.showToast(@StringRes res: Int) = Toasty.showShort(this, res)
23 |
24 | /**
25 | * Show long-time toast with String
26 | *
27 | * @param message original string
28 | */
29 | @AnyThread
30 | fun Context.showLongToast(message: String) = Toasty.showLong(this, message)
31 |
32 | /**
33 | * Show long-time toast with String
34 | *
35 | * @param res string's resource id
36 | */
37 | @AnyThread
38 | fun Context.showLongToast(@StringRes res: Int) = Toasty.showLong(this, res)
39 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/extensions/DoubleExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.extensions
2 |
3 | import java.math.BigDecimal
4 | import java.math.RoundingMode
5 |
6 | /**
7 | * Keep two decimal places after rounding
8 | *
9 | * @return Double with two decimal
10 | */
11 | fun Double.to2Decimal(): Double {
12 | return runCatching {
13 | BigDecimal.valueOf(this).setScale(2, RoundingMode.HALF_UP).toDouble()
14 | }.getOrDefault(0.0)
15 | }
16 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/extensions/FileExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.extensions
2 |
3 | import com.qhy040404.libraryonetap.utils.HashUtils
4 | import java.io.File
5 |
6 | /**
7 | * Calculate a File's MD5 hash
8 | *
9 | * @return MD5 hash of the File
10 | */
11 | fun File.md5() = HashUtils.md5(this.readBytes())
12 |
13 | /**
14 | * Calculate a File's SHA-1 hash
15 | *
16 | * @return SHA-1 hash of the File
17 | */
18 | fun File.sha1() = HashUtils.sha1(this.readBytes())
19 |
20 | /**
21 | * Calculate a File's SHA-256 hash
22 | *
23 | * @return SHA-256 hash of the File
24 | */
25 | fun File.sha256() = HashUtils.sha256(this.readBytes())
26 |
27 | /**
28 | * Calculate a File's SHA-512 hash
29 | *
30 | * @return SHA-512 hash of the File
31 | */
32 | fun File.sha512() = HashUtils.sha512(this.readBytes())
33 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/extensions/IntExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.extensions
2 |
3 | import android.content.Context
4 | import android.graphics.drawable.Drawable
5 | import com.qhy040404.libraryonetap.LibraryOneTapApp
6 | import com.qhy040404.libraryonetap.constant.GlobalValues
7 |
8 | /**
9 | * Get string from resource ID
10 | */
11 | fun Int.getString(): String {
12 | val context = LibraryOneTapApp.app
13 | val conf = context.resources.configuration.apply {
14 | setLocale(GlobalValues.locale)
15 | }
16 | return context.createConfigurationContext(conf).getString(this)
17 | }
18 |
19 | /**
20 | * Get format string from resource ID and format with provided params
21 | */
22 | fun Int.getStringAndFormat(vararg params: Any?): String {
23 | return this.getString().format(*params)
24 | }
25 |
26 | /**
27 | * Get color from resource ID
28 | */
29 | fun Int.getColor(context: Context): Int {
30 | return context.getColor(this)
31 | }
32 |
33 | /**
34 | * Get drawable from resource ID
35 | */
36 | fun Int.getDrawable(context: Context): Drawable? {
37 | return context.getDrawable(this)
38 | }
39 |
40 | /**
41 | * Get dimen from resource ID
42 | */
43 | fun Int.getDimen(): Float {
44 | return LibraryOneTapApp.app.resources.getDimension(this)
45 | }
46 |
47 | /**
48 | * Convert an Integer from one digit to two digits
49 | */
50 | fun Int.one2two(): String {
51 | return if (this >= 10) {
52 | "$this"
53 | } else {
54 | "0$this"
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/extensions/UriExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.extensions
2 |
3 | import android.content.Context
4 | import android.content.Intent
5 | import android.net.Uri
6 | import androidx.browser.customtabs.CustomTabsIntent
7 |
8 | /**
9 | * Try to start Uri with CustomTabsIntent, if failed, start it with traditional Intent
10 | *
11 | * @param context Context
12 | */
13 | fun Uri.start(
14 | context: Context,
15 | onFailure: () -> Unit = {
16 | val intent = Intent(Intent.ACTION_VIEW).apply {
17 | data = this@start
18 | }
19 | runCatching {
20 | context.startActivity(intent)
21 | }
22 | }
23 | ) {
24 | runCatching {
25 | CustomTabsIntent.Builder().build()
26 | .launchUrl(context, this)
27 | }.onFailure {
28 | onFailure()
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/extensions/ViewExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.extensions
2 |
3 | import android.animation.Animator
4 | import android.animation.TimeInterpolator
5 | import android.animation.ValueAnimator
6 | import android.content.Context
7 | import android.graphics.BitmapFactory
8 | import android.view.animation.AccelerateDecelerateInterpolator
9 | import androidx.appcompat.widget.AppCompatImageView
10 | import androidx.viewpager2.widget.ViewPager2
11 | import coil.load
12 | import com.qhy040404.libraryonetap.utils.AppUtils
13 | import com.qhy040404.libraryonetap.utils.QRUtils
14 |
15 | fun ViewPager2.setCurrentItem(
16 | item: Int,
17 | duration: Long,
18 | interpolator: TimeInterpolator = AccelerateDecelerateInterpolator(),
19 | pagePxWidth: Int = width
20 | ) {
21 | val pxToDrag: Int = pagePxWidth * (item - currentItem)
22 | val animator = ValueAnimator.ofInt(0, pxToDrag)
23 | var previousValue = 0
24 | animator.addUpdateListener { valueAnimator ->
25 | val currentValue = valueAnimator.animatedValue as Int
26 | val currentPxToDrag = (currentValue - previousValue).toFloat()
27 | fakeDragBy(-currentPxToDrag)
28 | previousValue = currentValue
29 | }
30 | animator.addListener(object : Animator.AnimatorListener {
31 | override fun onAnimationStart(animation: Animator) {
32 | beginFakeDrag()
33 | }
34 |
35 | override fun onAnimationEnd(animation: Animator) {
36 | endFakeDrag()
37 | }
38 |
39 | override fun onAnimationCancel(animation: Animator) {}
40 | override fun onAnimationRepeat(animation: Animator) {}
41 | })
42 | animator.interpolator = interpolator
43 | animator.duration = duration
44 | animator.start()
45 | }
46 |
47 | /**
48 | * Auto detect night mode to generate a nicer QR code for ImageView
49 | *
50 | * @param context Context
51 | * @param origByteArray Original QR code byte array
52 | */
53 | fun AppCompatImageView.mLoad(context: Context, origByteArray: ByteArray) {
54 | val qrCode = BitmapFactory.decodeByteArray(origByteArray, 0, origByteArray.size)
55 | load(
56 | if (AppUtils.currentIsNightMode(context)) {
57 | QRUtils.createWhiteBorderBitmap(qrCode, 2)
58 | } else {
59 | qrCode
60 | }
61 | )
62 | }
63 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/lazy/ResettableLazy.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.lazy
2 |
3 | import java.util.LinkedList
4 | import kotlin.reflect.KProperty
5 |
6 | fun resettableLazy(
7 | manager: ResettableLazyManager,
8 | init: () -> T
9 | ): ResettableLazy = ResettableLazy(manager, init)
10 |
11 | fun resettableManager(): ResettableLazyManager = ResettableLazyManager()
12 |
13 | interface Resettable {
14 | fun reset()
15 | }
16 |
17 | class ResettableLazy(
18 | private val manager: ResettableLazyManager,
19 | val init: () -> T
20 | ) : Resettable {
21 | @Volatile
22 | var lazyHolder = makeInitBlock()
23 |
24 | operator fun getValue(thisRef: Any?, property: KProperty<*>): T = lazyHolder.value
25 |
26 | override fun reset() {
27 | lazyHolder = makeInitBlock()
28 | }
29 |
30 | private fun makeInitBlock(): Lazy {
31 | return lazy {
32 | manager.register(this)
33 | init()
34 | }
35 | }
36 | }
37 |
38 | class ResettableLazyManager {
39 | private val managedDelegates = LinkedList()
40 |
41 | fun register(managed: Resettable) {
42 | synchronized(managedDelegates) {
43 | managedDelegates.add(managed)
44 | }
45 | }
46 |
47 | fun reset() {
48 | synchronized(managedDelegates) {
49 | managedDelegates.forEach { it.reset() }
50 | managedDelegates.clear()
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/library/ReserveUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.library
2 |
3 | import com.qhy040404.libraryonetap.utils.TimeUtils
4 | import com.qhy040404.libraryonetap.utils.extensions.substringBetween
5 |
6 | object ReserveUtils {
7 | fun getResetRoomCode(space: String): Int {
8 | val area = space.substringBefore("图书馆")
9 | val room = space.substringBetween("图书馆", "阅")
10 | return RoomUtils.getRoomCode(area, room)
11 | }
12 |
13 | fun constructPara(room: Int) = "room_id=$room&order_date=${TimeUtils.getToday("/", false)}"
14 |
15 | fun constructParaForAddCode(seatId: String) =
16 | "seat_id=$seatId&order_date=${TimeUtils.getToday("/", false)}"
17 |
18 | fun constructParaForFinalReserve(addCode: String) = "addCode=$addCode&method=addSeat"
19 |
20 | fun formatAvailableMap(am: String) = am.trim()
21 | .replace("[[", "")
22 | .replace("]]", "")
23 | .replace("{", "")
24 | .replace("}", "")
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/library/RoomUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.library
2 |
3 | import com.qhy040404.libraryonetap.utils.RandomDataUtils
4 |
5 | object RoomUtils {
6 | fun getRoomCode(area: String, room: String) = when (area) {
7 | "伯川", "Bochuan" -> when (room) {
8 | "301" -> 168
9 | "312" -> 170
10 | "401" -> 195
11 | "404" -> 197
12 | "409" -> 196
13 | "501" -> 198
14 | "504" -> 199
15 | "507" -> 200
16 | else -> {
17 | val temp = intArrayOf(168, 170, 195, 197, 196, 198, 199, 200)
18 | temp[RandomDataUtils.getNum(8)]
19 | }
20 | }
21 |
22 | "令希", "Lingxi" -> when (room) {
23 | "301" -> 207
24 | "302" -> 208
25 | "401" -> 205
26 | "402" -> 206
27 | "501" -> 203
28 | "502" -> 204
29 | "601" -> 201
30 | "602" -> 202
31 | else -> {
32 | val temp = intArrayOf(201, 202, 203, 204, 205, 206, 207, 208)
33 | temp[RandomDataUtils.getNum(8)]
34 | }
35 | }
36 |
37 | else -> 0
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/migration/BaseMigration.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.migration
2 |
3 | import jonathanfinerty.once.Once
4 |
5 | abstract class BaseMigration(private val commit: String) {
6 | protected abstract val reason: String
7 | protected abstract fun migrate()
8 |
9 | fun doMigration() {
10 | if (Once.beenDone(commit)) return
11 | migrate()
12 | Once.markDone(commit)
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/migration/Migration.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.migration
2 |
3 | import com.qhy040404.libraryonetap.LibraryOneTapApp
4 | import com.qhy040404.libraryonetap.constant.Constants
5 | import java.io.File
6 |
7 | object Migration {
8 | fun checkAndMigrate() {
9 | MIGRATION_CHANGELOG.doMigration()
10 | }
11 |
12 | private val MIGRATION_CHANGELOG = object : BaseMigration("5e22380b") {
13 | override val reason = "Local changelog was saved as HTML string, new version uses " +
14 | "MarkdownParser to parse changelog. As a result, an Exception will be thrown because " +
15 | "of MarkdownParser cannot parse a HTML string."
16 |
17 | override fun migrate() {
18 | File(LibraryOneTapApp.app.dataDir, Constants.CHANGELOG_INACTIVE).delete()
19 | File(LibraryOneTapApp.app.dataDir, Constants.CHANGELOG_ACTIVE).delete()
20 | }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/status/AppStatusHelper.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.status
2 |
3 | import android.app.Activity
4 | import android.app.Application
5 | import android.os.Bundle
6 |
7 | object AppStatusHelper {
8 | private var mOnAppStatusListener: OnAppStatusListener? = null
9 |
10 | fun register(app: Application, listener: OnAppStatusListener) {
11 | mOnAppStatusListener = listener
12 | app.registerActivityLifecycleCallbacks(activityLifecycleCallbacks)
13 | }
14 |
15 | private val activityLifecycleCallbacks = object : Application.ActivityLifecycleCallbacks {
16 | private var activityStartCount = 0
17 | override fun onActivityCreated(p0: Activity, p1: Bundle?) {}
18 | override fun onActivityStarted(p0: Activity) {
19 | activityStartCount++
20 | if (activityStartCount == 1) {
21 | mOnAppStatusListener?.onFront()
22 | }
23 | }
24 |
25 | override fun onActivityResumed(p0: Activity) {}
26 | override fun onActivityPaused(p0: Activity) {}
27 | override fun onActivityStopped(p0: Activity) {
28 | activityStartCount--
29 | if (activityStartCount == 0) {
30 | mOnAppStatusListener?.onBack()
31 | }
32 | }
33 |
34 | override fun onActivitySaveInstanceState(p0: Activity, p1: Bundle) {}
35 | override fun onActivityDestroyed(p0: Activity) {}
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/status/OnAppStatusListener.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.status
2 |
3 | interface OnAppStatusListener {
4 | fun onFront()
5 | fun onBack()
6 | }
7 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/tools/GPAUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.tools
2 |
3 | import com.qhy040404.libraryonetap.constant.enums.GPAAlgorithm
4 | import com.qhy040404.libraryonetap.data.tools.Grade
5 | import com.qhy040404.libraryonetap.utils.extensions.to2Decimal
6 |
7 | object GPAUtils {
8 | fun calculateGPA(grades: List, algorithm: GPAAlgorithm): Double {
9 | var totalWeightedGP = 0.0
10 | var totalCredits = grades.sumOf { it.credit }
11 | grades.forEach {
12 | runCatching {
13 | totalWeightedGP += when (algorithm) {
14 | GPAAlgorithm.DLUT -> it.gp * it.credit
15 | GPAAlgorithm.STD5 -> getGpByStandard5(it.grade.toInt()) * it.credit
16 | GPAAlgorithm.STD4 -> getGpByStandard4(it.grade.toInt()) * it.credit
17 | GPAAlgorithm.PK4 -> getGpByPeking4(it.grade.toInt()) * it.credit
18 | }
19 | }.onFailure { _ ->
20 | totalCredits -= it.credit
21 | }
22 | }
23 | return (totalWeightedGP / totalCredits).to2Decimal()
24 | }
25 |
26 | private fun getGpByStandard5(score: Int): Double {
27 | return when (score) {
28 | in 95..100 -> 5.0
29 | in 90 until 95 -> 4.5
30 | in 85 until 90 -> 4.0
31 | in 80 until 85 -> 3.5
32 | in 75 until 80 -> 3.0
33 | in 70 until 75 -> 2.5
34 | in 65 until 70 -> 2.0
35 | in 60 until 65 -> 1.0
36 | else -> 0.0
37 | }
38 | }
39 |
40 | private fun getGpByStandard4(score: Int): Double {
41 | return when (score) {
42 | in 90..100 -> 4.0
43 | in 80 until 90 -> 3.0
44 | in 70 until 80 -> 2.0
45 | in 60 until 70 -> 1.0
46 | else -> 0.0
47 | }
48 | }
49 |
50 | private fun getGpByPeking4(score: Int): Double {
51 | return when (score) {
52 | in 90..100 -> 4.0
53 | in 85 until 90 -> 3.7
54 | in 82 until 85 -> 3.3
55 | in 78 until 82 -> 3.0
56 | in 75 until 78 -> 2.7
57 | in 72 until 75 -> 2.3
58 | in 68 until 72 -> 2.0
59 | in 64 until 68 -> 1.5
60 | in 60 until 64 -> 1.0
61 | else -> 0.0
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/tools/GetPortalData.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.tools
2 |
3 | import com.qhy040404.libraryonetap.constant.Constants
4 | import com.qhy040404.libraryonetap.constant.GlobalValues
5 | import com.qhy040404.libraryonetap.constant.URLManager
6 | import com.qhy040404.libraryonetap.utils.web.Requests
7 |
8 | object GetPortalData {
9 | /**
10 | * mode 0:electric
11 | * mode 1:net
12 | */
13 | fun getPortalData(mode: Int): String {
14 | Requests.loginSso(
15 | URLManager.PORTAL_SSO_URL,
16 | GlobalValues.ctSso,
17 | URLManager.PORTAL_SSO_URL
18 | ).let {
19 | if (it.not()) {
20 | return GlobalValues.ssoPrompt
21 | }
22 | }
23 | Requests.get(URLManager.PORTAL_DIRECT_URL)
24 |
25 | return when (mode) {
26 | 0 -> Requests.post(
27 | URLManager.PORTAL_ELEC_URL,
28 | Constants.PORTAL_DEFAULT_POST,
29 | GlobalValues.ctJson
30 | )
31 |
32 | 1 -> Requests.post(
33 | URLManager.PORTAL_NET_URL,
34 | Constants.PORTAL_DEFAULT_POST,
35 | GlobalValues.ctJson
36 | )
37 |
38 | else -> throw IllegalArgumentException("Unknown mode")
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/tools/GradesUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.tools
2 |
3 | import android.content.Context
4 | import com.absinthe.libraries.utils.extensions.getStringArray
5 | import com.qhy040404.libraryonetap.R
6 | import com.qhy040404.libraryonetap.constant.Constants
7 | import com.qhy040404.libraryonetap.constant.GlobalValues
8 | import com.qhy040404.libraryonetap.constant.enums.GPAAlgorithm
9 | import com.qhy040404.libraryonetap.constant.enums.Parentheses
10 | import com.qhy040404.libraryonetap.data.tools.Grade
11 | import com.qhy040404.libraryonetap.utils.extensions.addParentheses
12 | import com.qhy040404.libraryonetap.utils.extensions.to2Decimal
13 |
14 | object GradesUtils {
15 | fun calculateWeightedAverage(
16 | grades: List
17 | ): Double {
18 | var totalWeightedSum = 0.0
19 | var totalCredits = grades.sumOf { it.credit }
20 | grades.forEach {
21 | runCatching {
22 | totalWeightedSum += it.grade.toDouble() * it.credit
23 | }.onFailure { _ ->
24 | totalCredits -= it.credit
25 | }
26 | }
27 | return (totalWeightedSum / totalCredits).to2Decimal()
28 | }
29 |
30 | fun calculateAverageGP(
31 | context: Context,
32 | grades: List
33 | ): String {
34 | return when (GlobalValues.gpOption) {
35 | Constants.GPA_DLUT -> {
36 | GPAUtils.calculateGPA(grades, GPAAlgorithm.DLUT)
37 | .toString() + getCurrentGPAAlgorithm(context).addParentheses(Parentheses.SMALL)
38 | }
39 |
40 | Constants.GPA_STANDARD5 -> {
41 | GPAUtils.calculateGPA(grades, GPAAlgorithm.STD5)
42 | .toString() + getCurrentGPAAlgorithm(context).addParentheses(Parentheses.SMALL)
43 | }
44 |
45 | Constants.GPA_STANDARD4 -> {
46 | GPAUtils.calculateGPA(grades, GPAAlgorithm.STD4)
47 | .toString() + getCurrentGPAAlgorithm(context).addParentheses(Parentheses.SMALL)
48 | }
49 |
50 | Constants.GPA_PEKING4 -> {
51 | GPAUtils.calculateGPA(grades, GPAAlgorithm.PK4)
52 | .toString() + getCurrentGPAAlgorithm(context).addParentheses(Parentheses.SMALL)
53 | }
54 |
55 | else -> {
56 | GPAUtils.calculateGPA(grades, GPAAlgorithm.DLUT)
57 | .toString() + getCurrentGPAAlgorithm(context).addParentheses(Parentheses.SMALL)
58 | }
59 | }
60 | }
61 |
62 | private fun getCurrentGPAAlgorithm(context: Context): String =
63 | context.getStringArray(R.array.gp)[
64 | context.getStringArray(R.array.gp_values)
65 | .indexOf(GlobalValues.gpOption)
66 | ]
67 | }
68 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/tools/VolunteerUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.tools
2 |
3 | import com.qhy040404.libraryonetap.utils.extensions.toJson
4 |
5 | object VolunteerUtils {
6 | fun createVolunteerPostData(name: String, id: String) =
7 | VolunteerDataClass(name, id).toJson()!!
8 |
9 | data class VolunteerDataClass(
10 | val name: String? = null,
11 | val stu_id: String? = null
12 | )
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/web/CookieJarImpl.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.web
2 |
3 | import okhttp3.Cookie
4 | import okhttp3.CookieJar
5 | import okhttp3.HttpUrl
6 |
7 | object CookieJarImpl : CookieJar {
8 | private val cookieStore = hashMapOf>()
9 |
10 | override fun saveFromResponse(url: HttpUrl, cookies: List) {
11 | if (cookieStore[url.host] == null) cookieStore[url.host] = hashSetOf()
12 | cookieStore[url.host]?.replaceIfExists(cookies)
13 | }
14 |
15 | override fun loadForRequest(url: HttpUrl): List {
16 | return cookieStore[url.host]?.toList() ?: emptyList()
17 | }
18 |
19 | private fun MutableSet.replaceIfExists(newData: Collection) {
20 | this.removeAll(newData.toSet())
21 | this.addAll(newData)
22 | }
23 |
24 | fun reset() {
25 | cookieStore.clear()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/utils/web/WebVPNUtils.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.utils.web
2 |
3 | import com.qhy040404.libraryonetap.constant.GlobalValues
4 | import com.qhy040404.libraryonetap.constant.URLManager
5 | import com.qhy040404.libraryonetap.utils.AppUtils
6 | import com.qhy040404.libraryonetap.utils.encrypt.AesEncryptUtils
7 |
8 | object WebVPNUtils {
9 | fun init() {
10 | if (!AppUtils.checkData(GlobalValues.id, GlobalValues.passwd)) {
11 | return
12 | }
13 | val apiPostData =
14 | "schoolcode=dlut&username=${GlobalValues.id}&password=${GlobalValues.passwd}&ssokey="
15 | Requests.post(URLManager.WEBVPN_INIT_URL, apiPostData, GlobalValues.ctVCard)
16 | }
17 |
18 | fun encryptUrl(url: String): String {
19 | val protocol: String
20 | var port = ""
21 |
22 | var mUrl = if (url.startsWith("http://")) {
23 | protocol = "http"
24 | url.removePrefix("http://")
25 | } else if (url.startsWith("https://")) {
26 | protocol = "https"
27 | url.removePrefix("https://")
28 | } else {
29 | throw IllegalArgumentException("Illegal url")
30 | }
31 |
32 | var v6 = ""
33 | Regex("\\[[\\da-fA-F:]+?]").find(mUrl).let {
34 | if (it != null) {
35 | v6 = it.value
36 | mUrl = mUrl.substring(v6.length)
37 | }
38 | }
39 | val segments = mUrl.substringBefore('?').split(':')
40 | if (segments.size > 1) {
41 | port = segments[1].substringBefore('/')
42 | mUrl = mUrl.substring(0, segments[0].length) +
43 | mUrl.substring(segments[0].length + port.length + 1)
44 | }
45 |
46 | val i = mUrl.indexOf('/')
47 | if (i == -1) {
48 | if (v6.isNotEmpty()) {
49 | mUrl = v6
50 | }
51 | mUrl = AesEncryptUtils.vpnEncrypt(mUrl, "wrdvpnisthebest!", "wrdvpnisthebest!")
52 | } else {
53 | var host = mUrl.substring(0, i)
54 | val path = mUrl.substring(i)
55 | if (v6.isNotEmpty()) {
56 | host = v6
57 | }
58 | mUrl = AesEncryptUtils.vpnEncrypt(host, "wrdvpnisthebest!", "wrdvpnisthebest!") + path
59 | }
60 | return if (port.isNotEmpty()) {
61 | "${URLManager.WEBVPN_INSTITUTION_URL}/$protocol-$port/$mUrl"
62 | } else {
63 | "${URLManager.WEBVPN_INSTITUTION_URL}/$protocol/$mUrl"
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/view/PasswordPreference.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.view
2 |
3 | import android.content.Context
4 | import android.graphics.Typeface
5 | import android.text.InputType
6 | import android.text.method.PasswordTransformationMethod
7 | import android.util.AttributeSet
8 | import androidx.annotation.AttrRes
9 | import androidx.annotation.StyleRes
10 | import com.qhy040404.libraryonetap.utils.encrypt.DesEncryptUtils
11 | import com.takisoft.preferencex.EditTextPreference
12 |
13 | @Suppress("unused")
14 | class PasswordPreference : EditTextPreference {
15 | constructor(context: Context) : super(context)
16 | constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)
17 | constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(
18 | context,
19 | attrs,
20 | defStyleAttr
21 | )
22 |
23 | constructor(
24 | context: Context,
25 | attrs: AttributeSet?,
26 | @AttrRes defStyleAttr: Int,
27 | @StyleRes defStyleRes: Int
28 | ) : super(context, attrs, defStyleAttr, defStyleRes)
29 |
30 | init {
31 | if (summaryProvider is SimpleSummaryProvider) {
32 | summaryProvider = PasswordSummaryProvider
33 | }
34 | setOnBindEditTextListener { editText ->
35 | editText.inputType =
36 | InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
37 | editText.typeface = Typeface.DEFAULT
38 | text?.let { editText.setSelection(it.length) }
39 | }
40 | }
41 |
42 | override fun setText(text: String?) {
43 | return if (text?.length!! > 16) {
44 | super.setText(text)
45 | } else {
46 | super.setText(DesEncryptUtils.strEnc(text, "q", "h", "y"))
47 | }
48 | }
49 |
50 | override fun getText(): String? {
51 | val currentText = super.getText()
52 | return if (currentText == null) {
53 | null
54 | } else {
55 | DesEncryptUtils.strDec(currentText, "q", "h", "y")
56 | }
57 | }
58 |
59 | object PasswordSummaryProvider : SummaryProvider {
60 | override fun provideSummary(preference: EditTextPreference): CharSequence? {
61 | val text = preference.text
62 | return if (!text.isNullOrEmpty()) {
63 | PasswordTransformationMethod.getInstance().getTransformation(text, null)
64 | } else {
65 | SimpleSummaryProvider.getInstance().provideSummary(preference)
66 | }
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/app/src/main/java/com/qhy040404/libraryonetap/view/ToastView.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap.view
2 |
3 | import android.content.Context
4 | import android.graphics.Color
5 | import android.view.Gravity
6 | import android.view.ViewGroup
7 | import androidx.appcompat.widget.AppCompatImageView
8 | import androidx.appcompat.widget.AppCompatTextView
9 | import com.absinthe.libraries.utils.view.AViewGroup
10 | import com.qhy040404.libraryonetap.R
11 |
12 | /**
13 | * From Absinthe
14 | * @author Absinthe, qhy040404
15 | */
16 | class ToastView(context: Context) : AViewGroup(context) {
17 | val message = AppCompatTextView(context).apply {
18 | layoutParams =
19 | LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
20 | maxWidth = 300.dp
21 | gravity = Gravity.CENTER
22 | setTextAppearance(android.R.style.TextAppearance_Material_Body2)
23 | setTextColor(Color.BLACK)
24 | val padding = 12.dp
25 | setPadding(padding, padding, padding, padding)
26 | setBackgroundResource(R.drawable.bg_toast)
27 | addView(this)
28 | }
29 |
30 | private val icon = AppCompatImageView(context).apply {
31 | layoutParams = LayoutParams(24.dp, 24.dp)
32 | setImageResource(R.drawable.pic_splash)
33 | addView(this)
34 | }
35 |
36 | override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
37 | super.onMeasure(widthMeasureSpec, heightMeasureSpec)
38 | message.autoMeasure()
39 | icon.autoMeasure()
40 | setMeasuredDimension(
41 | message.measuredWidth,
42 | message.measuredHeight + icon.measuredHeight / 2
43 | )
44 | }
45 |
46 | override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
47 | icon.let { it.layout(it.toHorizontalCenter(this), 0) }
48 | message.layout(0, icon.measuredHeight / 2)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-night/bg_fs_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/bg_toast.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxxhdpi/pic_splash.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/drawable-xxxhdpi/pic_splash.webp
--------------------------------------------------------------------------------
/app/src/main/res/drawable/bg_fs_dialog.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_about.xml:
--------------------------------------------------------------------------------
1 |
7 |
14 |
21 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_about_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
72 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_cache.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
17 |
22 |
27 |
32 |
37 |
42 |
43 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_card.xml:
--------------------------------------------------------------------------------
1 |
7 |
14 |
21 |
28 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_changelog.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
19 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_color_palette.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
15 |
18 |
21 |
24 |
27 |
30 |
33 |
36 |
39 |
42 |
45 |
46 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_dark_mode.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_elec.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_exams.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_github.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_gpa.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_grades.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_issue.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
17 |
22 |
27 |
32 |
37 |
42 |
47 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_language.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
6 |
11 |
16 |
21 |
26 |
31 |
36 |
41 |
46 |
51 |
56 |
61 |
66 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_lessons.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_material.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_multi.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
17 |
22 |
27 |
32 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_passwd.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
17 |
22 |
27 |
32 |
37 |
38 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_refresh.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sc_exams.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sc_qr.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
13 |
16 |
19 |
22 |
25 |
28 |
31 |
34 |
37 |
40 |
43 |
46 |
49 |
52 |
57 |
58 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sc_tool.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_sc_vcard.xml:
--------------------------------------------------------------------------------
1 |
6 |
10 |
17 |
24 |
31 |
38 |
39 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_single.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
15 |
16 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_tools.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
17 |
22 |
27 |
32 |
37 |
42 |
47 |
52 |
57 |
62 |
67 |
72 |
77 |
82 |
83 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_update.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_userid.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_volunteer.xml:
--------------------------------------------------------------------------------
1 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/simplepage_card.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 | -
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/splash_drawable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | -
5 |
6 |
7 |
8 |
9 |
10 |
11 | -
12 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/white_back_btn.xml:
--------------------------------------------------------------------------------
1 |
16 |
22 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
13 |
14 |
22 |
23 |
29 |
30 |
31 |
40 |
41 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_vcard.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
13 |
20 |
21 |
27 |
28 |
29 |
39 |
40 |
54 |
55 |
66 |
67 |
76 |
77 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_reserve.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
17 |
18 |
25 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_yanxiujian.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
23 |
24 |
34 |
35 |
45 |
46 |
55 |
56 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/preference_recyclerview.xml:
--------------------------------------------------------------------------------
1 |
2 |
21 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/simplepage_activity.xml:
--------------------------------------------------------------------------------
1 |
8 |
9 |
14 |
15 |
20 |
21 |
22 |
31 |
32 |
39 |
40 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/bottom_nav_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/grade_menu.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/dcmt.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-xhdpi/dcmt.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/qhy040404_avatar.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-xhdpi/qhy040404_avatar.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/values-night-v31/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @android:color/system_accent1_100
4 | @android:color/system_neutral1_900
5 | @android:color/system_neutral1_700
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/color_fullscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @color/white
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #90CAF9
4 | #90CAF9
5 | #FF121212
6 | #FFFFFFFF
7 | #80BEBEBE
8 |
9 | #FF121212
10 | #50FFFFFF
11 | #25000000
12 | #25FFFFFF
13 |
14 | @color/material_blue_800
15 |
16 | #CCBBBBBB
17 | #FFFFFFFF
18 | #CCBBBBBB
19 | #FFFFFFFF
20 |
21 | @color/blue_500
22 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/colors_simplepage.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #282828
4 | #363636
5 | @color/simple_page_card_text_color
6 | @color/simple_page_hint
7 | #deffffff
8 | @color/simple_page_card_text_color
9 | #80ffffff
10 | #abaaaa
11 | #141414
12 | #5C000000
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values-night/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v31/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @android:color/system_accent1_600
4 | @android:color/system_neutral1_50
5 | @android:color/system_neutral1_100
6 |
7 | @android:color/system_accent1_0
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v34/bools.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | true
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values-zh-rCN/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - 大工算法
5 | - 标准5分
6 | - 标准4分
7 | - 北大4分
8 |
9 |
10 |
11 |
12 | - 随机主题
13 | - 馆藏蓝
14 | - 苍穹蓝
15 | - 少女粉
16 | - 青葱绿
17 | - 活力橙
18 | - 淡雅红
19 | - 基佬紫
20 | - 简约风
21 |
22 |
23 |
24 | - 跟随系统
25 | - 开启
26 | - 关闭
27 |
28 |
29 |
30 | - 跟随系统
31 | - 简体中文
32 | - English
33 |
34 |
35 |
36 |
37 | - 令希
38 | - 伯川
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/values-zh-rCN/formats.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | "暂离截止: %s"
4 |
5 |
6 | 本学期已获得学分: %1$.1f\n加权均分: %2$.2f 平均绩点: %3$s
7 | %1$s\n分数: %2$s\n学分: %3$.1f\n绩点: %4$.1f
8 |
9 |
10 | 时间: %1$s\n考场: %2$s
11 |
12 |
13 | 共 %1$d 门课\n总学分: %2$.1f
14 | 教师: %1$s\n教学班: %2$s\n学分: %3$.1f\n类型: %4$s
15 |
16 |
17 | "发现新版本: %s"
18 |
19 |
20 | 余额: %1$s 元\n已用流量: %2$s GB\n剩余流量: %3$s GB
21 | %1$s\n剩余电量: %2$s 度
22 | "数据更新至: %s"
23 |
24 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - DLUT algorithm
5 | - Standard quintile algorithm
6 | - Standard quarter algorithm
7 | - Peking University quarter algorithm
8 |
9 |
10 |
11 |
12 | - Random
13 | - Library
14 | - Blue
15 | - Pink
16 | - Green
17 | - Orange
18 | - Red
19 | - Purple
20 | - Simple
21 |
22 |
23 |
24 | - Follow system
25 | - On
26 | - Off
27 |
28 |
29 |
30 | - Follow system
31 | - 简体中文
32 | - English
33 |
34 |
35 |
36 |
37 | - Lingxi
38 | - Bochuan
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/values/bools.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/color_custom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF1B6EE0
4 | #FF104287
5 | #FF0C3165
6 | #FFBB86FC
7 | #FF6200EE
8 | #FF3700B3
9 | #FFA6C6EE
10 | #FF288AF6
11 | #FF0A32C8
12 | #FFFEC8D6
13 | #FFFF69B4
14 | #FFFF1493
15 | #FF7FFFAA
16 | #FF00FA9A
17 | #FF00FF7F
18 | #FFFFE0B2
19 | #FFFF9800
20 | #FFF57C00
21 | #FFFFBFC6
22 | #FFF44336
23 | #FFD32F2F
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/values/color_fullscreen.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | @color/black
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF000000
4 | #FFFFFFFF
5 | #00000000
6 | #FFFFFF
7 | #FF104287
8 | #121212
9 |
10 | @color/blue_500
11 | @color/blue_500
12 | @color/blue_500
13 | #FF000000
14 | #80464646
15 |
16 | #FFDFDFDF
17 | #10000000
18 | #15000000
19 | #40000000
20 |
21 | #B1B8DF
22 | @color/material_blue_100
23 |
24 | #CCDFDFDF
25 | #CC444444
26 | #CC888888
27 | #CC444444
28 |
29 | #33000000
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors_simplepage.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #ffffff
4 | #44000000
5 | @color/simple_page_card_text_color
6 | @color/simple_page_hint
7 | #de000000
8 | @android:color/black
9 | #757575
10 | #757575
11 | #F1F1F1
12 | #5C000000
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 24dp
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/formats.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | %s %s v%s (%d)
4 |
5 |
6 | %s v%s (%d)
7 |
8 |
9 | "Temporary leave deadline: %s"
10 | order_id: %1$s\n\n%2$s\n\n%3$s\n%4$s\n%5$s\n%6$s\n
11 |
12 |
13 | Credits earned this semester: %1$.1f\nWeighted average score: %2$.2f GPA: %3$s
14 | %1$s\nScore: %2$s\nCredit: %3$.1f\nGP: %4$.1f
15 |
16 |
17 | Time: %1$s\nRoom: %2$s
18 |
19 |
20 | Count: %1$d\nTotal Credits: %2$.1f
21 | Teacher: %1$s\nClass code: %2$s\nCredit: %3$.1f\nType: %4$s
22 |
23 |
24 | "Update available: %s"
25 |
26 |
27 | Balance: %1$s Yuan\nUsed network: %2$s GB\nRemain network: %3$s GB
28 | %1$s\nRemain electric: %2$s degree
29 | "Updated: %s"
30 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ids.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
19 |
20 |
21 |
22 |
23 |
24 |
35 |
36 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/values/themes_overlay.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values/untranslatable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Material Design 3
5 |
6 |
7 | Library One Tap
8 |
9 |
10 |
11 |
12 | - dlut
13 | - std5
14 | - std4
15 | - peking4
16 |
17 |
18 |
19 |
20 | - random
21 | - library
22 | - blue
23 | - pink
24 | - green
25 | - orange
26 | - red
27 | - purple
28 | - simple
29 |
30 |
31 |
32 | - system
33 | - on
34 | - off
35 |
36 |
37 |
38 | - system
39 | - zh-CN
40 | - en-US
41 |
42 |
43 |
44 |
45 | - @string/df_random
46 | - 301
47 | - 302
48 | - 401
49 | - 402
50 | - 501
51 | - 502
52 | - 601
53 | - 602
54 |
55 |
56 |
57 | - @string/df_random
58 | - 301
59 | - 312
60 | - 401
61 | - 404
62 | - 409
63 | - 501
64 | - 504
65 | - 507
66 |
67 |
68 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/filepaths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/shortcuts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
12 |
13 |
14 |
19 |
23 |
24 |
25 |
30 |
34 |
35 |
36 |
41 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/tools_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
16 |
17 |
21 |
22 |
26 |
27 |
31 |
32 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/test/README.md:
--------------------------------------------------------------------------------
1 | # Unit Test
2 |
3 | This is used to test if function works properly and `env` has been set properly.
4 |
5 |
--------------------------------------------------------------------------------
/app/src/test/java/com/qhy040404/libraryonetap/AesTest.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap
2 |
3 | import com.qhy040404.libraryonetap.utils.web.WebVPNUtils
4 | import org.junit.Assert
5 | import org.junit.Test
6 |
7 | class AesTest {
8 | @Test
9 | fun test() {
10 | Assert.assertEquals(
11 | "https://webvpn.dlut.edu.cn/http/77726476706e69737468656265737421e4f2409f2f7e6c5c6b1cc7a99c406d3690/",
12 | WebVPNUtils.encryptUrl("http://teach.dlut.edu.cn/")
13 | )
14 | Assert.assertEquals(
15 | "https://webvpn.dlut.edu.cn/https/77726476706e69737468656265737421e7e056d2233c7d44300d8db9d6562d/",
16 | WebVPNUtils.encryptUrl("https://www.dlut.edu.cn/")
17 | )
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/test/java/com/qhy040404/libraryonetap/ConstantsTest.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap
2 |
3 | import org.junit.Assert.assertNotEquals
4 | import org.junit.Assert.assertNotNull
5 | import org.junit.Test
6 |
7 | class ConstantsTest {
8 | @Test
9 | fun hasBuglyAppId() {
10 | assertNotNull(BuildConfig.BUGLY_APPID)
11 | assertNotEquals("", BuildConfig.BUGLY_APPID)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/app/src/test/java/com/qhy040404/libraryonetap/DatetimeTest.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap
2 |
3 | import com.qhy040404.datetime.Datetime
4 | import com.qhy040404.datetime.Datetime.Companion.toDatetime
5 | import com.qhy040404.datetime.DatetimePart
6 | import org.junit.Assert
7 | import org.junit.Test
8 |
9 | class DatetimeTest {
10 | private val before1 = "2022-01-01T08:00:00".toDatetime()
11 | private val before2 = "2022-01-01T08:00:00".toDatetime()
12 | private val now = Datetime.now()
13 |
14 | @Test
15 | fun testCompareTo() {
16 | Assert.assertTrue(before1 < now)
17 | Assert.assertTrue(now > before1)
18 | Assert.assertTrue(before2 < now)
19 | Assert.assertTrue(now > before2)
20 | Assert.assertTrue(before2 == before1)
21 | Assert.assertTrue(before1 == before2)
22 | }
23 |
24 | @Test
25 | fun testConvert() {
26 | Assert.assertTrue(before1 == Datetime.fromInstant(before1.toInstant()))
27 | Assert.assertTrue(before1 == Datetime.fromTimestamp(before1.toTimestamp()))
28 | Assert.assertTrue(before1 == before1.toString().toDatetime())
29 |
30 | val beforeAddYear = "2023-01-01T08:00:00".toDatetime()
31 | Assert.assertTrue(beforeAddYear == before1.plus(1, DatetimePart.YEAR))
32 |
33 | val beforeMinusYear = "2021-01-01T08:00:00".toDatetime()
34 | Assert.assertTrue(beforeMinusYear == before1.minus(1, DatetimePart.YEAR))
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/app/src/test/java/com/qhy040404/libraryonetap/DesTest.kt:
--------------------------------------------------------------------------------
1 | package com.qhy040404.libraryonetap
2 |
3 | import com.qhy040404.libraryonetap.utils.encrypt.DesEncryptUtils
4 | import org.junit.Assert
5 | import org.junit.Test
6 |
7 | class DesTest {
8 | @Test
9 | fun testEncAndDec() {
10 | val testStringA = "ThisIsATest"
11 | val testStringB = "AnotherKeyTest"
12 | val testStringC = "LongKeyTest"
13 | val testString = "ConstantTest"
14 | Assert.assertEquals(
15 | testStringA,
16 | DesEncryptUtils.strDec(
17 | DesEncryptUtils.strEnc(testStringA, "1", "2", "3"),
18 | "1",
19 | "2",
20 | "3"
21 | )
22 | )
23 | Assert.assertEquals(
24 | testStringB,
25 | DesEncryptUtils.strDec(
26 | DesEncryptUtils.strEnc(testStringB, "a", "b", "cc"),
27 | "a",
28 | "b",
29 | "cc"
30 | )
31 | )
32 | Assert.assertEquals(
33 | testStringC,
34 | DesEncryptUtils.strDec(
35 | DesEncryptUtils.strEnc(testStringC, "a", "little", "longer"),
36 | "a",
37 | "little",
38 | "longer"
39 | )
40 | )
41 | Assert.assertEquals(
42 | "BBDED83F263993C4250BD1B81A688702C0A5C6CA237A670E",
43 | DesEncryptUtils.strEnc(testString, "1", "2", "3")
44 | )
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/build.gradle.kts:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | plugins {
3 | alias(libs.plugins.android.application) apply false
4 | alias(libs.plugins.android.library) apply false
5 | alias(libs.plugins.kotlin.android) apply false
6 | alias(libs.plugins.kotlinter) apply false
7 | alias(libs.plugins.resopt) apply false
8 | }
9 |
10 | allprojects {
11 | apply(plugin = rootProject.libs.plugins.kotlinter.get().pluginId)
12 | }
13 |
14 | task("clean") {
15 | delete(rootProject.layout.buildDirectory)
16 | }
17 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx6g -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 -XX:+UseParallelGC -XX:MaxMetaspaceSize=1g
2 | org.gradle.parallel=true
3 | org.gradle.caching=true
4 | org.gradle.configuration-cache=true
5 | android.enableAppCompileTimeRClass=true
6 | android.enableBuildConfigAsBytecode=true
7 | android.useAndroidX=true
8 | kotlin.code.style=official
9 | kotlin.build.report.output=build_scan
10 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.1-bin.zip
4 | networkTimeout=10000
5 | validateDistributionUrl=true
6 | zipStoreBase=GRADLE_USER_HOME
7 | zipStorePath=wrapper/dists
8 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 | @rem SPDX-License-Identifier: Apache-2.0
17 | @rem
18 |
19 | @if "%DEBUG%"=="" @echo off
20 | @rem ##########################################################################
21 | @rem
22 | @rem Gradle startup script for Windows
23 | @rem
24 | @rem ##########################################################################
25 |
26 | @rem Set local scope for the variables with windows NT shell
27 | if "%OS%"=="Windows_NT" setlocal
28 |
29 | set DIRNAME=%~dp0
30 | if "%DIRNAME%"=="" set DIRNAME=.
31 | @rem This is normally unused
32 | set APP_BASE_NAME=%~n0
33 | set APP_HOME=%DIRNAME%
34 |
35 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
36 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
37 |
38 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
39 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
40 |
41 | @rem Find java.exe
42 | if defined JAVA_HOME goto findJavaFromJavaHome
43 |
44 | set JAVA_EXE=java.exe
45 | %JAVA_EXE% -version >NUL 2>&1
46 | if %ERRORLEVEL% equ 0 goto execute
47 |
48 | echo. 1>&2
49 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
50 | echo. 1>&2
51 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
52 | echo location of your Java installation. 1>&2
53 |
54 | goto fail
55 |
56 | :findJavaFromJavaHome
57 | set JAVA_HOME=%JAVA_HOME:"=%
58 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
59 |
60 | if exist "%JAVA_EXE%" goto execute
61 |
62 | echo. 1>&2
63 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
64 | echo. 1>&2
65 | echo Please set the JAVA_HOME variable in your environment to match the 1>&2
66 | echo location of your Java installation. 1>&2
67 |
68 | goto fail
69 |
70 | :execute
71 | @rem Setup the command line
72 |
73 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
74 |
75 |
76 | @rem Execute Gradle
77 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
78 |
79 | :end
80 | @rem End local scope for the variables with windows NT shell
81 | if %ERRORLEVEL% equ 0 goto mainEnd
82 |
83 | :fail
84 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
85 | rem the _cmd.exe /c_ return code!
86 | set EXIT_CODE=%ERRORLEVEL%
87 | if %EXIT_CODE% equ 0 set EXIT_CODE=1
88 | if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
89 | exit /b %EXIT_CODE%
90 |
91 | :mainEnd
92 | if "%OS%"=="Windows_NT" endlocal
93 |
94 | :omega
95 |
--------------------------------------------------------------------------------
/qodana.yaml:
--------------------------------------------------------------------------------
1 | # Qodana configuration:
2 | # https://www.jetbrains.com/help/qodana/qodana-yaml.html
3 | version: 1.0
4 | linter: jetbrains/qodana-jvm-android
5 | profile:
6 | name: qodana.recommended
7 | projectJDK: corretto-11
8 | exclude:
9 | # Disable Gradle Kotlin DSL check
10 | - name: UnstableApiUsage
11 | paths:
12 | - app/build.gradle.kts
13 | - build.gradle.kts
14 | - settings.gradle.kts
15 | - name: AndroidLintAppBundleLocaleChanges
16 | - name: PropertyName
17 | paths:
18 | - app/src/main/java/com/qhy040404/libraryonetap/data
19 | - app/src/main/java/com/qhy040404/libraryonetap/utils/tools/VolunteerUtils.kt
20 | - name: AndroidLintUnusedResources
21 | - name: AndroidLintVectorPath
22 | - name: AndroidLintOverdraw
23 | - name: AndroidLintObsoleteSdkInt
24 | - name: AndroidLintMonochromeLauncherIcon
25 | - name: AndroidLintGradleDependency
26 |
--------------------------------------------------------------------------------
/settings.gradle.kts:
--------------------------------------------------------------------------------
1 | @file:Suppress("UnstableApiUsage")
2 |
3 | pluginManagement {
4 | repositories {
5 | gradlePluginPortal()
6 | google()
7 | mavenCentral()
8 | }
9 | }
10 |
11 | plugins {
12 | id("com.gradle.develocity") version "3.18.1"
13 | }
14 |
15 | dependencyResolutionManagement {
16 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
17 | repositories {
18 | google()
19 | mavenCentral()
20 | maven("https://jitpack.io")
21 | }
22 | }
23 |
24 | develocity {
25 | buildScan {
26 | termsOfUseUrl = "https://gradle.com/terms-of-service"
27 | termsOfUseAgree = "yes"
28 | // TODO: workaround for https://github.com/gradle/gradle/issues/22879.
29 | val isCI = providers.environmentVariable("CI").isPresent
30 | publishing.onlyIf { isCI }
31 | }
32 | }
33 |
34 | enableFeaturePreview("STABLE_CONFIGURATION_CACHE")
35 | enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
36 |
37 | rootProject.name = "Library-One-Tap"
38 | include(":app")
39 |
--------------------------------------------------------------------------------
/source/header.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/qhy040404/Library-One-Tap-Android/63799d543e2156c422c5f4c6c8f4bb14cdfb3f25/source/header.png
--------------------------------------------------------------------------------