From 57a0ecc385904b41d1bfcb6999993421907cba91 Mon Sep 17 00:00:00 2001 From: fankesyooni Date: Mon, 10 Feb 2025 03:05:25 +0800 Subject: [PATCH] Initial commit --- .editorconfig | 35 + .github/workflows/docs-deploy.yml | 57 + .gitignore | 107 + .idea/icon.png | Bin 0 -> 31546 bytes .idea/inspectionProfiles/Project_Default.xml | 10 + .idea/kotlinc.xml | 6 + .idea/ktlint-plugin.xml | 6 + .idea/migrations.xml | 10 + .idea/vcs.xml | 6 + LICENSE | 201 ++ README-zh-CN.md | 92 + README.md | 100 + build.gradle.kts | 64 + demo-android/build.gradle.kts | 54 + demo-android/proguard-rules.pro | 32 + .../pangutext/demo/ExampleInstrumentedTest.kt | 24 + demo-android/src/main/AndroidManifest.xml | 29 + .../pangutext/demo/ui/ListActivity.kt | 50 + .../pangutext/demo/ui/MainActivity.kt | 64 + .../pangutext/demo/ui/base/BaseActivity.kt | 37 + .../res/drawable/ic_launcher_foreground.xml | 31 + .../src/main/res/layout/activity_list.xml | 21 + .../src/main/res/layout/activity_main.xml | 168 ++ .../src/main/res/layout/adapter_list.xml | 12 + .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 31546 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 31546 bytes .../src/main/res/values-night/themes.xml | 11 + .../src/main/res/values-zh-rCN/strings.xml | 4 + demo-android/src/main/res/values/colors.xml | 8 + demo-android/src/main/res/values/strings.xml | 3 + demo-android/src/main/res/values/styles.xml | 6 + demo-android/src/main/res/values/themes.xml | 13 + .../pangutext/demo/ExampleUnitTest.kt | 17 + docs-source/.gitignore | 4 + docs-source/.vscode/settings.json | 3 + docs-source/build-dokka.sh | 4 + docs-source/package.json | 17 + docs-source/src/.vuepress/config.ts | 64 + docs-source/src/.vuepress/configs/template.ts | 146 ++ docs-source/src/.vuepress/configs/utils.ts | 39 + .../src/.vuepress/public/images/demo_01.png | Bin 0 -> 34646 bytes .../src/.vuepress/public/images/demo_02.gif | Bin 0 -> 152015 bytes .../src/.vuepress/public/images/demo_02.mov | Bin 0 -> 199468 bytes .../src/.vuepress/public/images/logo.png | Bin 0 -> 31546 bytes docs-source/src/.vuepress/styles/index.scss | 179 ++ docs-source/src/en/about/about.md | 27 + docs-source/src/en/about/changelog.md | 27 + docs-source/src/en/about/contacts.md | 16 + docs-source/src/en/about/future.md | 15 + docs-source/src/en/config/r8-proguard.md | 6 + docs-source/src/en/guide/home.md | 70 + docs-source/src/en/guide/quick-start.md | 86 + docs-source/src/en/index.md | 13 + docs-source/src/en/library/android.md | 367 +++ docs-source/src/en/library/compose.md | 9 + docs-source/src/index.md | 17 + docs-source/src/zh-cn/about/about.md | 27 + docs-source/src/zh-cn/about/changelog.md | 19 + docs-source/src/zh-cn/about/contacts.md | 15 + docs-source/src/zh-cn/about/future.md | 15 + docs-source/src/zh-cn/config/r8-proguard.md | 5 + docs-source/src/zh-cn/guide/home.md | 55 + docs-source/src/zh-cn/guide/quick-start.md | 83 + docs-source/src/zh-cn/index.md | 13 + docs-source/src/zh-cn/library/android.md | 365 +++ docs-source/src/zh-cn/library/compose.md | 9 + docs-source/yarn.lock | 2004 +++++++++++++++++ gradle.properties | 35 + .../sweet-dependency-config.yaml | 65 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 43583 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 252 +++ gradlew.bat | 94 + img-src/icon.png | Bin 0 -> 31546 bytes pangutext-android/build.gradle.kts | 49 + pangutext-android/consumer-rules.pro | 0 pangutext-android/proguard-rules.pro | 21 + .../pangutext/ExampleInstrumentedTest.kt | 25 + .../src/main/AndroidManifest.xml | 4 + .../pangutext/android/PanguText.kt | 189 ++ .../pangutext/android/PanguTextConfig.kt | 94 + .../pangutext/android/core/PanguMarginSpan.kt | 100 + .../pangutext/android/core/PanguPatterns.kt | 108 + .../pangutext/android/core/PanguTextView.kt | 39 + .../android/core/PanguTextWatcher.kt | 44 + .../pangutext/android/extension/PanguText.kt | 129 ++ .../android/extension/Replacement.kt | 110 + .../android/factory/PanguTextFactory2.kt | 131 ++ .../pangutext/android/factory/PanguWidget.kt | 136 ++ .../src/main/res/values/attrs.xml | 13 + pangutext-android/src/main/res/values/ids.xml | 4 + .../highcapable/pangutext/ExampleUnitTest.kt | 18 + settings.gradle.kts | 23 + 93 files changed, 6685 insertions(+) create mode 100644 .editorconfig create mode 100644 .github/workflows/docs-deploy.yml create mode 100644 .gitignore create mode 100644 .idea/icon.png create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 .idea/kotlinc.xml create mode 100644 .idea/ktlint-plugin.xml create mode 100644 .idea/migrations.xml create mode 100644 .idea/vcs.xml create mode 100644 LICENSE create mode 100644 README-zh-CN.md create mode 100644 README.md create mode 100644 build.gradle.kts create mode 100644 demo-android/build.gradle.kts create mode 100644 demo-android/proguard-rules.pro create mode 100644 demo-android/src/androidTest/java/com/highcapable/pangutext/demo/ExampleInstrumentedTest.kt create mode 100644 demo-android/src/main/AndroidManifest.xml create mode 100644 demo-android/src/main/java/com/highcapable/pangutext/demo/ui/ListActivity.kt create mode 100644 demo-android/src/main/java/com/highcapable/pangutext/demo/ui/MainActivity.kt create mode 100644 demo-android/src/main/java/com/highcapable/pangutext/demo/ui/base/BaseActivity.kt create mode 100644 demo-android/src/main/res/drawable/ic_launcher_foreground.xml create mode 100644 demo-android/src/main/res/layout/activity_list.xml create mode 100644 demo-android/src/main/res/layout/activity_main.xml create mode 100644 demo-android/src/main/res/layout/adapter_list.xml create mode 100644 demo-android/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 demo-android/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 demo-android/src/main/res/values-night/themes.xml create mode 100644 demo-android/src/main/res/values-zh-rCN/strings.xml create mode 100644 demo-android/src/main/res/values/colors.xml create mode 100644 demo-android/src/main/res/values/strings.xml create mode 100644 demo-android/src/main/res/values/styles.xml create mode 100644 demo-android/src/main/res/values/themes.xml create mode 100644 demo-android/src/test/java/com/highcapable/pangutext/demo/ExampleUnitTest.kt create mode 100644 docs-source/.gitignore create mode 100644 docs-source/.vscode/settings.json create mode 100644 docs-source/build-dokka.sh create mode 100644 docs-source/package.json create mode 100644 docs-source/src/.vuepress/config.ts create mode 100644 docs-source/src/.vuepress/configs/template.ts create mode 100644 docs-source/src/.vuepress/configs/utils.ts create mode 100644 docs-source/src/.vuepress/public/images/demo_01.png create mode 100644 docs-source/src/.vuepress/public/images/demo_02.gif create mode 100644 docs-source/src/.vuepress/public/images/demo_02.mov create mode 100644 docs-source/src/.vuepress/public/images/logo.png create mode 100644 docs-source/src/.vuepress/styles/index.scss create mode 100644 docs-source/src/en/about/about.md create mode 100644 docs-source/src/en/about/changelog.md create mode 100644 docs-source/src/en/about/contacts.md create mode 100644 docs-source/src/en/about/future.md create mode 100644 docs-source/src/en/config/r8-proguard.md create mode 100644 docs-source/src/en/guide/home.md create mode 100644 docs-source/src/en/guide/quick-start.md create mode 100644 docs-source/src/en/index.md create mode 100644 docs-source/src/en/library/android.md create mode 100644 docs-source/src/en/library/compose.md create mode 100644 docs-source/src/index.md create mode 100644 docs-source/src/zh-cn/about/about.md create mode 100644 docs-source/src/zh-cn/about/changelog.md create mode 100644 docs-source/src/zh-cn/about/contacts.md create mode 100644 docs-source/src/zh-cn/about/future.md create mode 100644 docs-source/src/zh-cn/config/r8-proguard.md create mode 100644 docs-source/src/zh-cn/guide/home.md create mode 100644 docs-source/src/zh-cn/guide/quick-start.md create mode 100644 docs-source/src/zh-cn/index.md create mode 100644 docs-source/src/zh-cn/library/android.md create mode 100644 docs-source/src/zh-cn/library/compose.md create mode 100644 docs-source/yarn.lock create mode 100644 gradle.properties create mode 100644 gradle/sweet-dependency/sweet-dependency-config.yaml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat create mode 100644 img-src/icon.png create mode 100644 pangutext-android/build.gradle.kts create mode 100644 pangutext-android/consumer-rules.pro create mode 100644 pangutext-android/proguard-rules.pro create mode 100644 pangutext-android/src/androidTest/java/com/highcapable/pangutext/ExampleInstrumentedTest.kt create mode 100644 pangutext-android/src/main/AndroidManifest.xml create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/PanguText.kt create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/PanguTextConfig.kt create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/core/PanguMarginSpan.kt create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/core/PanguPatterns.kt create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/core/PanguTextView.kt create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/core/PanguTextWatcher.kt create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/extension/PanguText.kt create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/extension/Replacement.kt create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/factory/PanguTextFactory2.kt create mode 100644 pangutext-android/src/main/java/com/highcapable/pangutext/android/factory/PanguWidget.kt create mode 100644 pangutext-android/src/main/res/values/attrs.xml create mode 100644 pangutext-android/src/main/res/values/ids.xml create mode 100644 pangutext-android/src/test/java/com/highcapable/pangutext/ExampleUnitTest.kt create mode 100644 settings.gradle.kts diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..d9b9b1a --- /dev/null +++ b/.editorconfig @@ -0,0 +1,35 @@ +# noinspection EditorConfigKeyCorrectness +[{*.kt,*.kts}] +ktlint_standard_annotation = disabled +ktlint_standard_filename = disabled +ktlint_standard_wrapping = disabled +ktlint_standard_import-ordering = enabled +ktlint_standard_max-line-length = disabled +ktlint_standard_multiline-if-else = disabled +ktlint_standard_argument-list-wrapping = disabled +ktlint_standard_parameter-list-wrapping = disabled +ktlint_standard_trailing-comma-on-declaration-site = disabled +ktlint_function_signature_body_expression_wrapping = multiline +ktlint_standard_string-template-indent = disabled +ktlint_standard_function-signature = disabled +ktlint_standard_trailing-comma-on-call-site = disabled +ktlint_standard_multiline-expression-wrapping = disabled +ktlint_standard_no-empty-first-line-in-class-body = disabled +ktlint_standard_if-else-wrapping = disabled +ktlint_standard_if-else-bracing = disabled +ktlint_standard_statement-wrapping = disabled +ktlint_standard_blank-line-before-declaration = disabled +ktlint_standard_no-empty-file = disabled +ktlint_standard_property-naming = disabled +ktlint_standard_function-naming = disabled +ktlint_standard_chain-method-continuation = disabled +ktlint_standard_class-signature = disabled +ktlint_standard_condition-wrapping = disabled +ktlint_standard_class-signature = disabled +ktlint_standard_no-trailing-spaces = disabled +ktlint_standard_multiline-loop = disabled +ij_continuation_indent_size = 2 +indent_size = 4 +indent_style = space +insert_final_newline = false +max_line_length = 150 \ No newline at end of file diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml new file mode 100644 index 0000000..bb6cecc --- /dev/null +++ b/.github/workflows/docs-deploy.yml @@ -0,0 +1,57 @@ +name: Deploy to GitHub pages + +on: + workflow_dispatch: + push: + branches: [ main ] + paths: + - 'pangutext-android/src/**' + - 'docs-source/**' + - '.github/workflows/**' + +permissions: + contents: write + pages: write + id-token: write + +jobs: + docs: + if: ${{ success() }} + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 18 + - name: Prepare Java 17 + uses: actions/setup-java@v3 + with: + java-version: 17 + java-package: jdk + distribution: 'temurin' + cache: 'gradle' + - name: Cache Gradle Dependencies + uses: actions/cache@v3 + with: + path: | + ~/.gradle/caches + ~/.gradle/wrapper + !~/.gradle/caches/build-cache-* + key: gradle-deps-core-${{ hashFiles('**/build.gradle.kts') }} + restore-keys: | + gradle-deps + - name: Build VuePress site + run: | + cd docs-source + yarn -i + yarn docs:build-gh-pages + - name: Deploy to GitHub Pages + uses: crazy-max/ghaction-github-pages@v4 + with: + target_branch: gh-pages + build_dir: docs-source/dist + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3842e2d --- /dev/null +++ b/.gitignore @@ -0,0 +1,107 @@ +## Fully .gtignore for IntelliJ, Android Studio and Gradle based Java projects +## References: +## - https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +## - https://github.com/android/platform-samples/blob/main/.gitignore + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +.idea/.name +.idea/artifacts +.idea/compiler.xml +.idea/jarRepositories.xml +.idea/modules.xml +.idea/*.iml +.idea/modules +.idea/caches +.idea/material_theme** +.idea/other.xml +*.iml +*.ipr + +# Misc +.idea/misc.xml + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +# Android studio 3.1+ additional +.idea/deployment*.xml +.idea/assetWizardSettings.xml +.idea/androidTestResultsUserPreferences.xml + +# Android projects +**/local.properties +/captures +.externalNativeBuild +.cxx + +# Gradle projects +.gradle +build/ + +# Mkdocs temporary serving folder +docs-gen +site +*.bak +.idea/appInsightsSettings.xml + +# Mac OS +.DS_Store \ No newline at end of file diff --git a/.idea/icon.png b/.idea/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..55e4d67cdfa6b46a5d0d21ef524e303076c4ca4d GIT binary patch literal 31546 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 s%k literal 0 HcmV?d00001 diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000..146ab09 --- /dev/null +++ b/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,10 @@ + + + + \ No newline at end of file diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml new file mode 100644 index 0000000..c224ad5 --- /dev/null +++ b/.idea/kotlinc.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/ktlint-plugin.xml b/.idea/ktlint-plugin.xml new file mode 100644 index 0000000..53c26b4 --- /dev/null +++ b/.idea/ktlint-plugin.xml @@ -0,0 +1,6 @@ + + + + MANUAL + + \ No newline at end of file diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..261eeb9 --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README-zh-CN.md b/README-zh-CN.md new file mode 100644 index 0000000..7adf872 --- /dev/null +++ b/README-zh-CN.md @@ -0,0 +1,92 @@ +# Pangu Text + +[![GitHub license](https://img.shields.io/github/license/BetterAndroid/android-app-template?color=blue)](https://github.com/BetterAndroid/android-app-template/blob/main/LICENSE) +[![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram)](https://t.me/BetterAndroid) +[![Telegram](https://img.shields.io/badge/discussion%20dev-Telegram-blue.svg?logo=telegram)](https://t.me/HighCapable_Dev) +[![QQ](https://img.shields.io/badge/discussion%20dev-QQ-blue.svg?logo=tencent-qq&logoColor=red)](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf) + +LOGO + +一个中日韩 (CJK) 与英文单词、半角数字排版的解决方案。 + +[English](README.md) | 简体中文 + +| LOGO | [BetterAndroid](https://github.com/BetterAndroid) | +|---------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------| + +这个项目属于上述组织,**点击上方链接关注这个组织**,发现更多好项目。 + +## 项目缘由 + +这个项目的起因是因为直到目前为止还没有一套公开的方案能够完美解决中文、日文、韩文与英文之间的排版问题, +正常情况下我们将 CJK (即中日韩) 与英文混排的时候,都会涉及到美观性问题,这算是一个历史遗留问题,全角文字与半角文字之间的书写规范不一样。虽然现在 W3C 规定了 +CJK 排版规范, +但是还是仅有部分愿意遵守排版要求的个人或企业选择了这种方案。 + +目前已知的厂商解决方案如下 + +- Apple 全系 (iOS、iPadOS、macOS、tvOS、watchOS) 文本排版解决方案 +- 小米 (HyperOS) 文本排版优化 +- OrginOS 基于字体的文本排版优化 + +但是这些方案都是封闭的,无法在其他平台上使用,因此我们希望能够提供一套开源的解决方案,能够适应各种场景、侵入性低且更容易集成,让更多的开发者能够使用这个方案来解决文本排版问题。 + +本项目得以进行的主要来源为 [pangu.js](https://github.com/vinta/pangu.js),它提供了一套 CJK 排版的正则,我们对其加以优化,实现各个平台不需要插入空格字符即可格式化文本排版的效果, +衷心感谢这个项目的开发者提供的方案,我们在这个方案上加以扩展,提供了更多解决方案的可能性。 + +## 效果 + +如你所见,`PanguText` 的排版方案并不是向 CJK 与英文单词之间插入空格来完成,而是使用每个平台对应的处理方案自动在这些字符之间添加空白间距来达到排版效果以达到最低的侵入性。 + +> 应用前 (上)、应用后 (下) + + + +> 动态应用 + + + +`PanguText` 支持动态应用,它允许你在输入文本的同时动态为每个字符添加空白间距。 + +## 开始使用 + +[点击这里](https://betterandroid.github.io/PanguText/zh-cn) 前往文档页面查看更多详细教程和内容。 + +## 项目推广 + + +
+

嘿,还请君留步!👋

+

这里有 Android 开发工具、UI 设计、Gradle 插件、Xposed 模块和实用软件等相关项目。

+

如果下方的项目能为你提供帮助,不妨为我点个 star 吧!

+

所有项目免费、开源,遵循对应开源许可协议。

+

→ 查看更多关于我的项目,请点击这里 ←

+
+ +## Star History + +![Star History Chart](https://api.star-history.com/svg?repos=BetterAndroid/PanguText&type=Date) + +## 许可证 + +- [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) + +``` +Apache License Version 2.0 + +Copyright (C) 2019 HighCapable + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + +版权所有 © 2019 HighCapable \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..60588f8 --- /dev/null +++ b/README.md @@ -0,0 +1,100 @@ +# Pangu Text + +[![GitHub license](https://img.shields.io/github/license/BetterAndroid/android-app-template?color=blue)](https://github.com/BetterAndroid/android-app-template/blob/main/LICENSE) +[![Telegram](https://img.shields.io/badge/discussion-Telegram-blue.svg?logo=telegram)](https://t.me/BetterAndroid) +[![Telegram](https://img.shields.io/badge/discussion%20dev-Telegram-blue.svg?logo=telegram)](https://t.me/HighCapable_Dev) +[![QQ](https://img.shields.io/badge/discussion%20dev-QQ-blue.svg?logo=tencent-qq&logoColor=red)](https://qm.qq.com/cgi-bin/qm/qr?k=Pnsc5RY6N2mBKFjOLPiYldbAbprAU3V7&jump_from=webapi&authKey=X5EsOVzLXt1dRunge8ryTxDRrh9/IiW1Pua75eDLh9RE3KXE+bwXIYF5cWri/9lf) + +LOGO + +A typographic solution for the optimal alignment of CJK characters, English words, and half-width digits. + +English | [简体中文](README-zh-CN.md) + +| LOGO | [BetterAndroid](https://github.com/BetterAndroid) | +|---------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------| + +This project belongs to the above-mentioned organization, **click the link above to follow this organization** and discover more good projects. + +## Project Reason + +This project was created because, until now, there hasn’t been a public solution to perfectly address the typography issues between Chinese, Japanese, +Korean, and English. +Typically, when mixing CJK (i.e. Chinese, Japanese, Korean) with English, aesthetic issues can arise—a historical legacy stemming from the differences +in writing conventions between full-width and half-width characters. Although the W3C has now established CJK typography guidelines, only a few +individuals or companies willing to adhere to these standards have adopted this approach. + +Currently, the known vendor solutions are as follows: + +- Apple platforms (iOS, iPadOS, macOS, tvOS, watchOS) text typography solutions +- Xiaomi’s (HyperOS) text typography optimization +- OrginOS’s font-based text typography optimization + +However, these solutions are closed and cannot be implemented on other platforms. +We aim to provide an open-source solution adaptable to various scenarios, featuring low intrusiveness and easy integration, allowing more developers +to effectively address text typography issues. + +The primary inspiration for this project comes from [pangu.js](https://github.com/vinta/pangu.js), which offers a set of regular expressions for CJK +typography. +We have optimized these solutions to format text across platforms without inserting extra space characters. We extend this approach further to explore +additional possibilities. + +Heartfelt thanks to the original developer of **pangu.js** for providing the foundational solution. + +## Effects + +As you can see, the typography scheme of `PanguText` does not work by simply inserting spaces between CJK characters and English words. +Instead, it leverages each platform's native handling to automatically add whitespace between these characters, ensuring minimal intrusion. + +> Before Applying (Top) vs. After Applying (Bottom) + + + +> Dynamic Application + + + +`PanguText` supports dynamic application, which means it can add whitespace gaps to each character on-the-fly as you input text. + +## Get Started + +[Click here](https://betterandroid.github.io/PanguText/en) go to the documentation page for more detailed tutorials and content. + +## Promotion + + +
+

Hey, please stay! 👋

+

Here are related projects such as Android development tools, UI design, Gradle plugins, Xposed Modules and practical software.

+

If the project below can help you, please give me a star!

+

All projects are free, open source, and follow the corresponding open source license agreement.

+

→ To see more about my projects, please click here ←

+
+ +## Star History + +![Star History Chart](https://api.star-history.com/svg?repos=BetterAndroid/PanguText&type=Date) + +## License + +- [Apache-2.0](https://www.apache.org/licenses/LICENSE-2.0) + +``` +Apache License Version 2.0 + +Copyright (C) 2019 HighCapable + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +``` + +Copyright © 2019 HighCapable \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..46f3b6e --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,64 @@ +import com.vanniktech.maven.publish.AndroidSingleVariantLibrary +import com.vanniktech.maven.publish.MavenPublishBaseExtension +import org.jetbrains.dokka.gradle.DokkaTask + +plugins { + autowire(libs.plugins.android.application) apply false + autowire(libs.plugins.android.library) apply false + autowire(libs.plugins.kotlin.android) apply false + autowire(libs.plugins.kotlin.dokka) apply false + autowire(libs.plugins.maven.publish) apply false +} + +libraryProjects { + afterEvaluate { + configure { + repositories { + val repositoryDir = gradle.gradleUserHomeDir + .resolve("highcapable-maven-repository") + .resolve("repository") + maven { + name = "HighCapableMavenReleases" + url = repositoryDir.resolve("releases").toURI() + } + maven { + name = "HighCapableMavenSnapShots" + url = repositoryDir.resolve("snapshots").toURI() + } + } + } + configure { + configure(AndroidSingleVariantLibrary(publishJavadocJar = false)) + } + } + tasks.withType().configureEach { + val configuration = """{ "footerMessage": "PanguText | Apache-2.0 License | Copyright (C) 2019 HighCapable" }""" + pluginsMapConfiguration.set(mapOf("org.jetbrains.dokka.base.DokkaBase" to configuration)) + } + tasks.register("publishKDoc") { + group = "documentation" + dependsOn("dokkaHtml") + doLast { + val docsDir = rootProject.projectDir + .resolve("docs-source") + .resolve("dist") + .resolve("KDoc") + .resolve(project.name) + if (docsDir.exists()) docsDir.deleteRecursively() else docsDir.mkdirs() + layout.buildDirectory.dir("dokka/html").get().asFile.copyRecursively(docsDir) + } + } +} + +fun libraryProjects(action: Action) { + val libraries = listOf( + Libraries.PANGUTEXT_ANDROID, + Libraries.PANGUTEXT_COMPOSE + ) + allprojects { if (libraries.contains(name)) action.execute(this) } +} + +object Libraries { + const val PANGUTEXT_ANDROID = "pangutext-android" + const val PANGUTEXT_COMPOSE = "pangutext-compose" +} \ No newline at end of file diff --git a/demo-android/build.gradle.kts b/demo-android/build.gradle.kts new file mode 100644 index 0000000..09c96a1 --- /dev/null +++ b/demo-android/build.gradle.kts @@ -0,0 +1,54 @@ +plugins { + autowire(libs.plugins.android.application) + autowire(libs.plugins.kotlin.android) +} + +android { + namespace = property.project.app.packageName + compileSdk = property.project.android.compileSdk + + defaultConfig { + applicationId = property.project.app.packageName + minSdk = property.project.android.minSdk + targetSdk = property.project.android.targetSdk + versionName = property.project.app.versionName + versionCode = property.project.app.versionCode + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + } + buildTypes { + release { + isMinifyEnabled = false + proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro") + } + } + compileOptions { + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 + } + kotlinOptions { + jvmTarget = "17" + freeCompilerArgs = listOf( + "-Xno-param-assertions", + "-Xno-call-assertions", + "-Xno-receiver-assertions" + ) + } + buildFeatures { + buildConfig = true + viewBinding = true + } +} + +dependencies { + implementation(projects.pangutextAndroid) + implementation(com.highcapable.betterandroid.ui.component) + implementation(com.highcapable.betterandroid.ui.extension) + implementation(com.highcapable.betterandroid.system.extension) + implementation(androidx.core.core.ktx) + implementation(androidx.appcompat.appcompat) + implementation(com.google.android.material.material) + implementation(androidx.constraintlayout.constraintlayout) + testImplementation(junit.junit) + androidTestImplementation(androidx.test.ext.junit) + androidTestImplementation(androidx.test.espresso.espresso.core) +} \ No newline at end of file diff --git a/demo-android/proguard-rules.pro b/demo-android/proguard-rules.pro new file mode 100644 index 0000000..e819dd1 --- /dev/null +++ b/demo-android/proguard-rules.pro @@ -0,0 +1,32 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle.kts. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile + +-assumenosideeffects class kotlin.jvm.internal.Intrinsics { + public static *** throwUninitializedProperty(...); + public static *** throwUninitializedPropertyAccessException(...); +} + +-keep class * extends android.app.Activity +-keep class * implements androidx.viewbinding.ViewBinding { + (); + *** inflate(android.view.LayoutInflater); +} \ No newline at end of file diff --git a/demo-android/src/androidTest/java/com/highcapable/pangutext/demo/ExampleInstrumentedTest.kt b/demo-android/src/androidTest/java/com/highcapable/pangutext/demo/ExampleInstrumentedTest.kt new file mode 100644 index 0000000..22ce155 --- /dev/null +++ b/demo-android/src/androidTest/java/com/highcapable/pangutext/demo/ExampleInstrumentedTest.kt @@ -0,0 +1,24 @@ +package com.highcapable.pangutext.demo + +import androidx.test.platform.app.InstrumentationRegistry +import androidx.test.ext.junit.runners.AndroidJUnit4 + +import org.junit.Test +import org.junit.runner.RunWith + +import org.junit.Assert.* + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.highcapable.pangutext", appContext.packageName) + } +} \ No newline at end of file diff --git a/demo-android/src/main/AndroidManifest.xml b/demo-android/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f7b8a70 --- /dev/null +++ b/demo-android/src/main/AndroidManifest.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo-android/src/main/java/com/highcapable/pangutext/demo/ui/ListActivity.kt b/demo-android/src/main/java/com/highcapable/pangutext/demo/ui/ListActivity.kt new file mode 100644 index 0000000..d5a312a --- /dev/null +++ b/demo-android/src/main/java/com/highcapable/pangutext/demo/ui/ListActivity.kt @@ -0,0 +1,50 @@ +/* + * PanguText - A typographic solution for the optimal alignment of CJK characters, English words, and half-width digits. + * Copyright (C) 2019 HighCapable + * https://github.com/BetterAndroid/PanguText + * + * Apache License Version 2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is created by fankes on 2025/2/9. + */ +package com.highcapable.pangutext.demo.ui + +import android.graphics.Color +import android.os.Bundle +import com.highcapable.betterandroid.ui.component.adapter.factory.bindAdapter +import com.highcapable.betterandroid.ui.extension.view.textColor +import com.highcapable.pangutext.demo.databinding.ActivityListBinding +import com.highcapable.pangutext.demo.databinding.AdapterListBinding +import com.highcapable.pangutext.demo.ui.base.BaseActivity + +class ListActivity : BaseActivity() { + + private val listData = List(100) { "这是第${it}条Data演示" } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.recyclerView.bindAdapter { + onBindData { listData } + onBindViews { binding, text, _ -> + binding.text.text = text + binding.text.textColor = Color.rgb( + (0..255).random(), + (0..255).random(), + (0..255).random() + ) + } + } + } +} \ No newline at end of file diff --git a/demo-android/src/main/java/com/highcapable/pangutext/demo/ui/MainActivity.kt b/demo-android/src/main/java/com/highcapable/pangutext/demo/ui/MainActivity.kt new file mode 100644 index 0000000..61be721 --- /dev/null +++ b/demo-android/src/main/java/com/highcapable/pangutext/demo/ui/MainActivity.kt @@ -0,0 +1,64 @@ +/* + * PanguText - A typographic solution for the optimal alignment of CJK characters, English words, and half-width digits. + * Copyright (C) 2019 HighCapable + * https://github.com/BetterAndroid/PanguText + * + * Apache License Version 2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is created by fankes on 2025/1/12. + */ +package com.highcapable.pangutext.demo.ui + +import android.os.Bundle +import android.text.method.LinkMovementMethod +import androidx.core.text.HtmlCompat +import com.highcapable.betterandroid.ui.component.insets.factory.handleOnWindowInsetsChanged +import com.highcapable.betterandroid.ui.component.insets.factory.setInsetsPadding +import com.highcapable.betterandroid.ui.extension.component.startActivity +import com.highcapable.pangutext.demo.databinding.ActivityMainBinding +import com.highcapable.pangutext.demo.ui.base.BaseActivity + +class MainActivity : BaseActivity() { + + private val demoText = HtmlCompat.fromHtml( + "今天下午,我去了一家新开的咖啡店,店里环境非常舒适,感觉很cozy。" + + "我点了一杯latte,坐在窗边,透过玻璃看着街上的人来人往。店员还特别热情," + + "给我推荐了一款很特别的chocolate cake,味道真是不错!
" + + "我发现现在很多人都喜欢在咖啡店里工作,几乎每桌都有laptop。我的旁边有一位女士,正在忙着处理emails。" + + "我想,这样的环境真是适合集中精力工作。
" + + "总的来说,今天的体验很不错,下次还想再来尝试其他的drinksdesserts
" + + "合word样式测试。
" + + "You can点击这里访问项目地址。", + HtmlCompat.FROM_HTML_MODE_LEGACY + ) + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding.root.handleOnWindowInsetsChanged(animated = true) { linearLayout, insetsWrapper -> + linearLayout.setInsetsPadding(insetsWrapper.safeDrawing) + } + listOf( + binding.textViewPanguText, + binding.textViewPanguTextCjkSpacingRatio, + binding.textViewNoPanguText + ).forEach { + it.movementMethod = LinkMovementMethod.getInstance() + it.text = demoText + } + binding.buttonJumpList.setOnClickListener { + startActivity() + } + } +} \ No newline at end of file diff --git a/demo-android/src/main/java/com/highcapable/pangutext/demo/ui/base/BaseActivity.kt b/demo-android/src/main/java/com/highcapable/pangutext/demo/ui/base/BaseActivity.kt new file mode 100644 index 0000000..bee2adc --- /dev/null +++ b/demo-android/src/main/java/com/highcapable/pangutext/demo/ui/base/BaseActivity.kt @@ -0,0 +1,37 @@ +/* + * PanguText - A typographic solution for the optimal alignment of CJK characters, English words, and half-width digits. + * Copyright (C) 2019 HighCapable + * https://github.com/BetterAndroid/PanguText + * + * Apache License Version 2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is created by fankes on 2025/2/10. + */ +package com.highcapable.pangutext.demo.ui.base + +import android.os.Bundle +import android.view.LayoutInflater +import androidx.viewbinding.ViewBinding +import com.highcapable.betterandroid.ui.component.activity.AppBindingActivity +import com.highcapable.pangutext.android.factory.PanguTextFactory2 + +open class BaseActivity : AppBindingActivity() { + + override fun onPrepareContentView(savedInstanceState: Bundle?): LayoutInflater { + val inflater = super.onPrepareContentView(savedInstanceState) + PanguTextFactory2.inject(inflater) + return inflater + } +} \ No newline at end of file diff --git a/demo-android/src/main/res/drawable/ic_launcher_foreground.xml b/demo-android/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..e42c27e --- /dev/null +++ b/demo-android/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,31 @@ + + + + + + + + \ No newline at end of file diff --git a/demo-android/src/main/res/layout/activity_list.xml b/demo-android/src/main/res/layout/activity_list.xml new file mode 100644 index 0000000..d7e4d4b --- /dev/null +++ b/demo-android/src/main/res/layout/activity_list.xml @@ -0,0 +1,21 @@ + + + + + + + \ No newline at end of file diff --git a/demo-android/src/main/res/layout/activity_main.xml b/demo-android/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..a334ff5 --- /dev/null +++ b/demo-android/src/main/res/layout/activity_main.xml @@ -0,0 +1,168 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +