├── .github └── workflows │ └── codeql-analysis.yml ├── .gitignore ├── .idea └── copyright │ ├── fei_android_gpl.xml │ └── profiles_settings.xml ├── LICENSE ├── README.md ├── aboutconfig └── libraries │ └── lib_opencc_android.json ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── ic_launcher-playstore.png │ ├── java │ └── com │ │ └── iatfei │ │ └── tsconverter │ │ ├── AboutActivity.java │ │ ├── ChineseTypes.kt │ │ ├── Constant.java │ │ ├── ConvTileService.java │ │ ├── ConvertPopupActivity.java │ │ ├── ConvertUtils.java │ │ ├── FirstStartupActivity.kt │ │ ├── MainActivity.java │ │ ├── OpenSourceActivity.kt │ │ ├── SettingsActivity.java │ │ └── SimpleConvert.kt │ └── res │ ├── drawable-hdpi │ ├── tutorial_textselection_1.webp │ ├── tutorial_textselection_2.webp │ └── tutorial_textselection_3.webp │ ├── drawable-mdpi │ ├── tutorial_textselection_1.webp │ ├── tutorial_textselection_2.webp │ └── tutorial_textselection_3.webp │ ├── drawable-xhdpi │ ├── tutorial_textselection_1.webp │ ├── tutorial_textselection_2.webp │ └── tutorial_textselection_3.webp │ ├── drawable-xxhdpi │ ├── tutorial_textselection_1.webp │ ├── tutorial_textselection_2.webp │ └── tutorial_textselection_3.webp │ ├── drawable-xxxhdpi │ ├── tutorial_textselection_1.webp │ ├── tutorial_textselection_2.webp │ └── tutorial_textselection_3.webp │ ├── drawable │ ├── ic_applogo_intro.xml │ ├── ic_baseline_info_24.xml │ ├── ic_baseline_refresh_24.xml │ ├── ic_launcher_background.xml │ └── ic_launcher_foreground.xml │ ├── layout-land │ ├── activity_main.xml │ └── content_main.xml │ ├── layout │ ├── activity_about.xml │ ├── activity_convert.xml │ ├── activity_convert_empty.xml │ ├── activity_convert_simp.xml │ ├── activity_convert_simple.xml │ ├── activity_convert_trad.xml │ ├── activity_main.xml │ ├── activity_open_source.xml │ ├── activity_settings.xml │ ├── content_main.xml │ └── view_preference_switch.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-anydpi-v26 │ └── ic_launcher.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── mipmap-xxxhdpi │ └── ic_launcher.png │ ├── raw │ └── charmap.bin │ ├── resources.properties │ ├── values-en-rUS │ └── strings.xml │ ├── values-night │ ├── color.xml │ └── styles.xml │ ├── values-v28 │ └── styles.xml │ ├── values-zh-rCN │ └── strings.xml │ ├── values │ ├── arrays.xml │ ├── colors.xml │ ├── strings.xml │ └── styles.xml │ └── xml │ ├── about.xml │ ├── backup_descriptor.xml │ └── settings.xml ├── banner.xcf ├── build.gradle ├── fastlane └── metadata │ └── android │ ├── en-US │ ├── changelogs │ │ ├── 15.txt │ │ └── 18.txt │ ├── full_description.txt │ ├── images │ │ ├── featureGraphic.png │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ ├── 4.png │ │ │ └── 5.png │ ├── short_description.txt │ └── title.txt │ ├── zh-CN │ ├── changelogs │ │ ├── 15.txt │ │ └── 18.txt │ ├── full_description.txt │ ├── images │ │ ├── featureGraphic.png │ │ ├── icon.png │ │ └── phoneScreenshots │ │ │ ├── 1.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ └── 4.png │ ├── short_description.txt │ └── title.txt │ └── zh-TW │ ├── changelogs │ ├── 15.txt │ └── 18.txt │ ├── full_description.txt │ ├── images │ ├── featureGraphic.png │ ├── icon.png │ └── phoneScreenshots │ │ ├── 1.png │ │ ├── 2.png │ │ ├── 3.png │ │ └── 4.png │ ├── short_description.txt │ └── title.txt ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── settings.gradle ├── tsconverter-icon.svg └── util ├── ChineseTypes.kt └── dict-gen.kt /.github/workflows/codeql-analysis.yml: -------------------------------------------------------------------------------- 1 | name: "CodeQL" 2 | 3 | on: 4 | push: 5 | branches: [master, ] 6 | pull_request: 7 | # The branches below must be a subset of the branches above 8 | branches: [master] 9 | schedule: 10 | - cron: '0 2 * * 3' 11 | 12 | jobs: 13 | analyse: 14 | name: Analyse 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v2 20 | with: 21 | # We must fetch at least the immediate parents so that if this is 22 | # a pull request then we can checkout the head. 23 | fetch-depth: 2 24 | 25 | # If this run was triggered by a pull request event, then checkout 26 | # the head of the pull request instead of the merge commit. 27 | - run: git checkout HEAD^2 28 | if: ${{ github.event_name == 'pull_request' }} 29 | 30 | # Initializes the CodeQL tools for scanning. 31 | - name: Initialize CodeQL 32 | uses: github/codeql-action/init@v1 33 | # Override language selection by uncommenting this and choosing your languages 34 | # with: 35 | # languages: go, javascript, csharp, python, cpp, java 36 | 37 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java). 38 | # If this step fails, then you should remove it and run the build manually (see below) 39 | - name: Autobuild 40 | uses: github/codeql-action/autobuild@v1 41 | 42 | # ℹ️ Command-line programs to run using the OS shell. 43 | # 📚 https://git.io/JvXDl 44 | 45 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines 46 | # and modify them (or add more) to build your code if your project 47 | # uses a compiled language 48 | 49 | #- run: | 50 | # make bootstrap 51 | # make release 52 | 53 | - name: Perform CodeQL Analysis 54 | uses: github/codeql-action/analyze@v1 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gradle files 2 | .gradle/ 3 | build/ 4 | 5 | # Local configuration files 6 | local.properties 7 | 8 | # Android Studio and IntelliJ IDEA files 9 | .idea/* 10 | *.iml 11 | *.iws 12 | *.ipr 13 | 14 | # unignore copyright files 15 | !.idea/copyright/ 16 | 17 | # Android Studio cache 18 | #.idea/caches/ 19 | #.idea/libraries/ 20 | #.idea/modules.xml 21 | #.idea/workspace.xml 22 | #.idea/gradle.xml 23 | #.idea/usage.statistics.xml 24 | #.idea/dictionaries/ 25 | #.idea/httpRequests/ 26 | 27 | # Keystore files 28 | *.jks 29 | *.keystore 30 | 31 | # Log files 32 | *.log 33 | 34 | # OS-specific files 35 | .DS_Store 36 | Thumbs.db 37 | 38 | # Build directories 39 | app/build/ 40 | */build/ 41 | 42 | # Proguard configuration files 43 | # not necessary for open-source app? 44 | # proguard-rules.pro 45 | 46 | # Miscellaneous 47 | *.apk 48 | *.ap_ 49 | *.dex 50 | *.class 51 | 52 | # Android NDK 53 | ndkBuild/ 54 | local.properties -------------------------------------------------------------------------------- /.idea/copyright/fei_android_gpl.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android OpenCC (Android 開放中文轉換) 2 | An implementation of OpenCC for Android for the conversion between different variants of Chinese, with auto detection & conversion. 3 | 4 | 爲 Android 使用者開發的 OpenCC 中文轉換器。支援自動轉換。 5 | 6 | My third project! Developed during the COVID-19 pandemic. 7 | 8 | Special thanks to BYVoid for the [OpenCC project](https://github.com/BYVoid/OpenCC), and qichuan for the [Android OpenCC Library](https://github.com/qichuan/android-opencc). 9 | Special thanks to Renn for code contribution. 10 | 11 | My app can be downloaded in the following three sources. Thanks to [Reproducible Builds](https://f-droid.org/docs/Reproducible_Builds/), apk from all sources are signed with my own keys, allowing cross-updates. 12 | 13 | 14 | Get it on Google Play 17 | 18 | 19 | 20 | Get it on F-Droid 22 | 23 | 24 | GitHub Releases: https://github.com/fei0316/OpenCC-android-app/releases 25 | 26 | ## Features 27 | 28 | - Automatically detect Traditional or Simplified Chinese to convert automatically (Easy Mode) 29 | - 10 conversion modes (see below for details) 30 | - Convert directly by selecting text in text field, selecting the option in menu, and replaces with converted text automatically. 31 | 32 | ## Conversion Modes 33 | 34 | ### Conversion Modes chart 35 | 36 | | ID | From | To | Chinese Variant (異體) | Word Use (用詞) | 37 | |:--:|:------------------:|:---------------------:|:---------------------------------------------------:|:--------------------------------------------:| 38 | | 1 | Simplified | Traditional | OpenCC standard (OpenCC 標準) | ❌ (no conversion) | 39 | | 2 | Simplified | Traditional | Convert to Taiwan variant (臺灣正體標準) | ❌ | 40 | | 3 | Simplified | Traditional | Convert to Hong Kong variant (香港繁體標準/香港小學學習字詞表標準) | ❌ | 41 | | 4 | Simplified | Traditional | Convert to Taiwan variant (臺灣正體標準) | Convert to Taiwan phrases (臺灣常用詞彙) | 42 | | 5 | Traditional | Simplified | ❌ | ❌ | 43 | | 6 | Traditional | Simplified | Convert from Taiwan variant (臺灣正體標準) | ❌ | 44 | | 7 | Traditional | Simplified | Convert from Hong Kong variant (香港繁體標準/香港小學學習字詞表標準) | ❌ | 45 | | 8 | Traditional | Simplified | Convert from Taiwan variant (臺灣正體標準) | Convert to Mainland China phrases (中國大陸常用詞彙) | 46 | | 9 | OpenCC Traditional | Taiwan Traditional | Convert to Taiwan variant (臺灣正體標準) | ❌ | 47 | | 10 | OpenCC Traditional | Hong Kong Traditional | Convert to Hong Kong variant (香港繁體標準/香港小學學習字詞表標準) | ❌ | 48 | 49 | 50 | ### Notes on OpenCC standard 51 | 52 | **Note:** "no conversion" for Chinese variants converts inputs into the OpenCC standard, and no particular governmental standards are followed. It is based the contributors' research, and aims to avoid situations where one character in a variant can be multiple characters in another variant by splitting up as much as possible. 53 | 54 | For example, the character 「臺」 is used for 「臺灣」 while 「台」 is used for 「天台」 in Taiwan standard, but in Hong Kong standard 「台」 is used for both. In this case, the OpenCC standard adopts the Taiwanese convention when translating from Simplified Chinese where 「台」 is always used for both. 55 | 56 | **注意:**「OpenCC 標準」的異體選項代表使用 OpenCC 標準而不依照任何政府標準。OpenCC 標準由貢獻者參照各領域的漢字異體用法得出,依照「能分則不合」的原則,避免一對多的問題。 57 | 58 | 以「台/臺」舉例: 59 | 60 | | 標準 | Taiwan | Rooftop | 61 | | -------------- | -------- | -------- | 62 | | 臺灣標準 | 臺灣 | 天台 | 63 | | 香港標準 | 台灣 | 天台 | 64 | | **OpenCC標準** | **臺灣** | **天台** | 65 | 66 | ### Example 67 | 68 | #### Simplified to Traditional 69 | 70 | ##### Original 71 | 72 | ``` 73 | 这个用户应该使用鼠标点击这里来查看东涌的涌浪的图片 74 | ``` 75 | 76 | ##### OpenCC Traditional 77 | 78 | ``` 79 | 這個用戶應該使用鼠標點擊這裏來查看東涌的涌浪的圖片 80 | ``` 81 | 82 | ##### Taiwan Traditional 83 | 84 | ``` 85 | 這個用戶應該使用鼠標點擊這裡來查看東湧的湧浪的圖片 86 | ``` 87 | 88 | ##### Hong Kong Traditional 89 | 90 | ``` 91 | 這個用户應該使用鼠標點擊這裏來查看東涌的湧浪的圖片 92 | ``` 93 | 94 | ##### Taiwan Traditional with Taiwan phrases 95 | 96 | ``` 97 | 這個使用者應該使用滑鼠點選這裡來檢視東湧的湧浪的圖片 98 | ``` 99 | 100 | **Note:** 「涌/湧」 is an exception to the OpenCC's aim to separating as much as possible, as the contributors based their research on old literary and dictionaries and ignored the special use case in Cantonese-speaking region. 101 | 102 | **註:**「涌/湧」是 OpenCC 「能分則不合」的例外,請見 https://zhuanlan.zhihu.com/p/104314323 103 | 104 | #### Traditional to Simplified 105 | 106 | ##### Correct Simplified Chinese Result 107 | 108 | ``` 109 | 我同学手中拿着一本有关自行车的名著 110 | ``` 111 | 112 | | Original Traditional Chinese | From OpenCC standard | From Taiwanese | From HK | From Taiwanese (CN phrase) | 113 | | ------------------------------------------------------ | -------------------------------------------------- | ------------------------------------------ | -------------------------------------------------- | ------------------------------------------ | 114 | | 我同學手中拿**着**一本有關腳踏車的名**著** (HK style) | 我同学手中拿**着**一本有关脚踏车的名**著** | 我同学手中拿**着**一本有关脚踏车的名**著** | 我同学手中拿**着**一本有关脚踏车的名**著** | 我同学手中拿**着**一本有关自行车的名**著** | 115 | | 我同學手中拿**著**一本有關腳踏車的名**著** (TW style) | 我同学手中拿**著**一本有关脚踏车的名**著** (wrong) | 我同学手中拿**着**一本有关脚踏车的名**著** | 我同学手中拿**著**一本有关脚踏车的名**著** (wrong) | 我同学手中拿**着**一本有关自行车的名**著** | 116 | 117 | Therefore, please always choose the origin text to the best of your knowledge and double check the result. Most discrepancies are small however, as demonstrated. 118 | 119 | #### Traditional Variant Conversion 120 | 121 | ##### Original (OpenCC standard) 122 | 123 | ``` 124 | 我拿着有關臺灣的名著 125 | ``` 126 | 127 | ##### Hong Kong Standard 128 | 129 | ``` 130 | 我拿着有關台灣的名著 131 | ``` 132 | 133 | ##### Taiwan Standard 134 | 135 | ``` 136 | 我拿著有關臺灣的名著 137 | ``` 138 | 139 | ## Todo 140 | 141 | - Wait for qichuan to update the library for the new Hong Kong variant updates no longer based on 香港小學學習字詞表 (OpenCC pull request #418) 142 | - Japanese New Kanji support? 143 | 144 | ## How to build 145 | Just clone the repository and open it in Android Studio. It *should* work... 146 | ### Notes on Easy Mode autodetection 147 | The charmap.bin in res/raw is generated with code in util. It takes in data from OpenCC's GitHub page and compile a Serialized HashMap. Use Kotlin compiler to compile these two files and then run it to get the bin file. Change package name as needed. 148 | This part of the code is not as clean as I wanted it to be, so pull requests are welcome! 149 | ``` 150 | ./kotlinc ../dict-gen.kt ./ChineseTypes.class -include-runtime -d dict-gen.jar 151 | java -jar ./dict-gen.jar 152 | ``` 153 | 154 | ## Any issues? 155 | 156 | For problems related to conversion database, please open an issue in OpenCC's [GitHub page](https://github.com/BYVoid/OpenCC). 157 | 158 | Otherwise, please open an issue here. 159 | 160 | This app's version numbers follows Romantic, [Sentimental Versioning](https://github.com/dominictarr/sentimental-versioning). 161 | In the version number `x.y.z`, `x` will be updated when significant changes are made, `y` be updated when small changes are made, and `z` is reserved for minor bugfixes. 162 | So, don't ask about SemVer, it doesn't really make sense for an app like this anyway. 163 | 164 | ## Licenses 165 | ``` 166 | Copyright (c) 2020-2025 Fei Kuan. 167 | 168 | This program is free software: you can redistribute it and/or modify 169 | it under the terms of the GNU General Public License as published by 170 | the Free Software Foundation, either version 3 of the License, or 171 | (at your option) any later version. 172 | 173 | This program is distributed in the hope that it will be useful, 174 | but WITHOUT ANY WARRANTY; without even the implied warranty of 175 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 176 | GNU General Public License for more details. 177 | 178 | You should have received a copy of the GNU General Public License 179 | along with this program. If not, see . 180 | 181 | ``` 182 | -------------------------------------------------------------------------------- /aboutconfig/libraries/lib_opencc_android.json: -------------------------------------------------------------------------------- 1 | { 2 | "uniqueId": "com.github.qichuan:android-opencc", 3 | "developers": [ 4 | { 5 | "name": "Zhang Qichuan", 6 | "organisationUrl": "https://zhangqichuan.com/" 7 | } 8 | ], 9 | "artifactVersion": "1.2.0", 10 | "licenses": [ 11 | "MIT" 12 | ], 13 | "description": "An Android library project for conversion between Traditional and Simplified Chinese ", 14 | "name": "lib-opencc-android", 15 | "website": "https://github.com/qichuan/android-opencc" 16 | } -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | //apply plugin: 'kotlin-android-extensions' 4 | apply plugin: 'com.mikepenz.aboutlibraries.plugin' 5 | 6 | android { 7 | compileSdk 35 8 | defaultConfig { 9 | manifestPlaceholders 10 | applicationId "com.iatfei.tsconverter" 11 | minSdkVersion 23 12 | targetSdkVersion 35 13 | versionCode 19 14 | versionName "3.1.1" 15 | resourceConfigurations += ['en-rUS', 'zh-rCN', 'zh-rHK', 'zh-rTW'] 16 | manifestPlaceholders.appName = "@string/app_name" 17 | } 18 | buildTypes { 19 | debug { 20 | manifestPlaceholders.appName = "DEBUG-Chinese Converter" 21 | versionNameSuffix "-debug" 22 | applicationIdSuffix ".debug" 23 | } 24 | release { 25 | signingConfig null 26 | minifyEnabled true 27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 28 | } 29 | } 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_17 32 | targetCompatibility JavaVersion.VERSION_17 33 | } 34 | buildFeatures { 35 | viewBinding true 36 | buildConfig true 37 | } 38 | androidResources { 39 | generateLocaleConfig true 40 | } 41 | aaptOptions { 42 | cruncherEnabled = false 43 | } 44 | namespace 'com.iatfei.tsconverter' 45 | } 46 | 47 | dependencies { 48 | implementation fileTree(dir: 'libs', include: ['*.jar']) 49 | implementation 'androidx.appcompat:appcompat:1.7.0' 50 | implementation 'androidx.constraintlayout:constraintlayout:2.2.1' 51 | implementation 'com.google.android.material:material:1.12.0' 52 | implementation 'com.github.qichuan:android-opencc:1.2.0' 53 | implementation 'androidx.preference:preference-ktx:1.2.1' 54 | implementation "com.mikepenz:aboutlibraries:11.6.3" 55 | implementation "androidx.core:core-ktx:1.15.0" 56 | implementation "com.github.AppIntro:AppIntro:6.3.1" 57 | implementation 'androidx.preference:preference-ktx:1.2.1' 58 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.8.7' 59 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7' 60 | implementation 'commons-io:commons-io:2.18.0' 61 | // coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.0' 62 | constraints { 63 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") { 64 | because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib") 65 | } 66 | implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") { 67 | because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib") 68 | } 69 | } 70 | } 71 | 72 | aboutLibraries { 73 | configPath = "aboutconfig" 74 | } 75 | java { 76 | toolchain { 77 | languageVersion = JavaLanguageVersion.of(17) 78 | } 79 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | -keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | -renamesourcefileattribute SourceFile 22 | 23 | -keep class com.iatfei.tsconverter.ChineseTypes 24 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 46 | 52 | 56 | 57 | 58 | 59 | 60 | 61 | 64 | 69 | 74 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 102 | 103 | 104 | 105 | 106 | 110 | 113 | 114 | 115 | 116 | 117 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/com/iatfei/tsconverter/AboutActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Fei Kuan. 3 | * 4 | * This file is part of Chinese Converter 5 | * (see ). 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | package com.iatfei.tsconverter; 22 | 23 | import android.content.Intent; 24 | import android.content.pm.PackageManager; 25 | import android.net.Uri; 26 | import android.os.Bundle; 27 | 28 | import androidx.appcompat.app.ActionBar; 29 | import androidx.appcompat.app.AppCompatActivity; 30 | import com.google.android.material.appbar.MaterialToolbar; 31 | import androidx.preference.Preference; 32 | import androidx.preference.PreferenceFragmentCompat; 33 | 34 | public class AboutActivity extends AppCompatActivity { 35 | 36 | @Override 37 | protected void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | setContentView(R.layout.activity_about); 40 | 41 | getSupportFragmentManager() 42 | .beginTransaction() 43 | .replace(R.id.about, new SettingsFragment()) 44 | .commit(); 45 | 46 | MaterialToolbar toolbar = findViewById(R.id.toolbar_about); 47 | setSupportActionBar(toolbar); 48 | ActionBar actionBar = getSupportActionBar(); 49 | if (actionBar != null) { 50 | actionBar.setDisplayHomeAsUpEnabled(true); 51 | } 52 | } 53 | 54 | public static class SettingsFragment extends PreferenceFragmentCompat { 55 | @Override 56 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 57 | addPreferencesFromResource(R.xml.about); 58 | 59 | String version = BuildConfig.VERSION_NAME + 60 | //"-" + BuildConfig.FLAVOR + 61 | " v" + BuildConfig.VERSION_CODE; 62 | Preference ver = findPreference("edit_text_preference_2"); 63 | if (ver != null) { 64 | ver.setSummary(version); 65 | } 66 | Preference license = findPreference("edit_text_preference_6"); 67 | if (license != null) { 68 | license.setIntent(new Intent(getActivity(),OpenSourceActivity.class)); 69 | } 70 | 71 | Intent emailIntent = new Intent(Intent.ACTION_SENDTO); 72 | emailIntent.setData(Uri.parse("mailto:")); 73 | PackageManager packageManager = requireActivity().getPackageManager(); 74 | if(emailIntent.resolveActivity(packageManager) == null){ 75 | Preference mail = findPreference("edit_text_preference_8"); 76 | if (mail != null) { 77 | mail.setIntent(null); 78 | } 79 | } 80 | 81 | Intent webpageIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("https://www.example.com")); 82 | if(webpageIntent.resolveActivity(packageManager) == null) { 83 | Preference web = findPreference("edit_text_preference_4"); 84 | Preference github = findPreference("edit_text_preference_3"); 85 | if (web != null) { 86 | web.setIntent(null); 87 | } 88 | if (github != null) { 89 | github.setIntent(null); 90 | } 91 | } 92 | } 93 | } 94 | } -------------------------------------------------------------------------------- /app/src/main/java/com/iatfei/tsconverter/ChineseTypes.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Fei Kuan. 3 | * 4 | * This file is part of Chinese Converter 5 | * (see ). 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /* 22 | * Special thanks to Renn on GitHub who contributed to this piece of code. 23 | * Copyright (c) 2021 Renn. Released under GNU GPL v3+. 24 | */ 25 | 26 | package com.iatfei.tsconverter 27 | 28 | enum class ChineseTypes { 29 | SIMPLIFIED_CHINESE, 30 | TRADITIONAL_CHINESE, 31 | JAPANESE, 32 | NONE 33 | } -------------------------------------------------------------------------------- /app/src/main/java/com/iatfei/tsconverter/Constant.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Fei Kuan. 3 | * 4 | * This file is part of Chinese Converter 5 | * (see ). 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | package com.iatfei.tsconverter; 22 | 23 | public class Constant { 24 | public static final int APPINTRO_LAST_UPDATE_VERSIONCODE = 18; 25 | public static final String PREF_MAIN_EASY_MODE = "main_simplemode"; 26 | public static final String PREF_PREVIOUS_STARTED_VERSION = "previous_started_ver"; 27 | public static final String PREF_SETTINGS_EASY_MODE = "switch_preference_1"; 28 | public static final String PREF_SETTINGS_AUTODETECT_MODE = "switch_preference_2"; 29 | public static final String PREF_SETTINGS_TRAD_MODE = "list_preference_1"; 30 | public static final String PREF_SETTINGS_SIMP_MODE = "list_preference_2"; 31 | public static final String PREF_SETTINGS_TTS_LANGUAGE = "list_preference_tts_lang"; 32 | public static final String PREF_SETTINGS_UI_LANGUAGE = "list_preference_ui_lang"; 33 | public static final String PREF_SETTINGS_TEXT_PROCESSING = "settings_text_processing_master_switch"; 34 | public static final String PREF_SETTINGS_DELETE_TEXT = "multi_select_list_preference_1"; 35 | 36 | public static final String TILE_CONVERT_INTENT_EXTRA = "fromTile"; 37 | 38 | public static final String CLIPBOARD_LABEL = "ConvertedChinese"; 39 | 40 | public static final String TTS_UTTERANCE_ID = "com.iatfei.tsconvert:MainActivity.editText_convert"; 41 | 42 | public static final int S2T = 1; 43 | public static final int S2TW = 2; 44 | public static final int S2HK = 3; 45 | public static final int S2TWP = 4; 46 | public static final int T2S = 5; 47 | public static final int TW2S = 6; 48 | public static final int HK2S = 7; 49 | public static final int TW2SP = 8; 50 | public static final int T2TW = 9; 51 | public static final int T2HK = 10; 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/iatfei/tsconverter/ConvTileService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Fei Kuan. 3 | * 4 | * This file is part of Chinese Converter 5 | * (see ). 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | package com.iatfei.tsconverter; 22 | 23 | import android.annotation.SuppressLint; 24 | import android.app.PendingIntent; 25 | import android.content.Intent; 26 | import android.os.Build; 27 | import android.service.quicksettings.Tile; 28 | import android.service.quicksettings.TileService; 29 | 30 | import androidx.annotation.RequiresApi; 31 | 32 | @RequiresApi(api = Build.VERSION_CODES.N) 33 | 34 | public class ConvTileService extends TileService { 35 | 36 | public ConvTileService() { 37 | 38 | } 39 | 40 | @Override 41 | public void onStartListening() { 42 | Tile tile = getQsTile(); 43 | if (tile != null) { 44 | tile.setState(Tile.STATE_INACTIVE); 45 | tile.updateTile(); 46 | } 47 | super.onStartListening(); 48 | } 49 | 50 | @SuppressLint("StartActivityAndCollapseDeprecated") 51 | @Override 52 | public void onClick() { 53 | Intent intent = new Intent(this, ConvertPopupActivity.class); 54 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 55 | intent.putExtra(Constant.TILE_CONVERT_INTENT_EXTRA, true); 56 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) { 57 | startActivityAndCollapse( 58 | PendingIntent.getActivity( 59 | this, 60 | 0, 61 | intent, 62 | PendingIntent.FLAG_IMMUTABLE 63 | ) 64 | ); 65 | } else { 66 | startActivityAndCollapse(intent); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /app/src/main/java/com/iatfei/tsconverter/ConvertPopupActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Fei Kuan. 3 | * 4 | * This file is part of Chinese Converter 5 | * (see ). 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | package com.iatfei.tsconverter; 22 | 23 | import androidx.appcompat.app.AppCompatActivity; 24 | import androidx.preference.PreferenceManager; 25 | 26 | import android.content.ClipData; 27 | import android.content.ClipboardManager; 28 | import android.content.Context; 29 | import android.content.Intent; 30 | import android.content.SharedPreferences; 31 | import android.os.Bundle; 32 | import android.widget.Button; 33 | import android.widget.RadioGroup; 34 | import android.widget.Toast; 35 | 36 | import java.util.Objects; 37 | 38 | public class ConvertPopupActivity extends AppCompatActivity { 39 | 40 | boolean tileClipboardAccessRequested = false; 41 | 42 | @Override 43 | protected void onCreate(Bundle savedInstanceState) { 44 | super.onCreate(savedInstanceState); 45 | 46 | Intent intent = getIntent(); 47 | String action = intent.getAction(); 48 | String intentType = intent.getType(); 49 | 50 | if (Intent.ACTION_SEND.equals(action) && intentType != null) { 51 | // reached here through share action 52 | CharSequence textTemp = ""; 53 | if ("text/plain".equals(intentType)) { 54 | String sharedText = intent.getStringExtra(Intent.EXTRA_TEXT); 55 | if (sharedText != null) { 56 | textTemp = sharedText; 57 | } 58 | } 59 | convHelper(true, false, textTemp); 60 | } else if (intent.getBooleanExtra(Constant.TILE_CONVERT_INTENT_EXTRA, false)) { 61 | // reached here through tile 62 | // open empty activity to trigger onWindowFocusChanged 63 | tileClipboardAccessRequested = true; 64 | setContentView(R.layout.activity_convert_empty); 65 | } else { 66 | // reached here through text selection menu 67 | convHelper(getIntent().getBooleanExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, false), 68 | false, getIntent().getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT)); 69 | } 70 | } 71 | 72 | public void onWindowFocusChanged(boolean hasFocus) { 73 | super.onWindowFocusChanged(hasFocus); 74 | // get clipboard data once in activity, then call convHelper like others 75 | if (hasFocus) { 76 | if (tileClipboardAccessRequested) { 77 | tileClipboardAccessRequested = false; 78 | ClipboardManager clipBoard = (ClipboardManager)getSystemService(CLIPBOARD_SERVICE); 79 | ClipData clipData = clipBoard.getPrimaryClip(); 80 | if (clipData == null) { 81 | return; 82 | } 83 | CharSequence cs = clipData.getItemAt(0).getText(); 84 | if (cs == null) { 85 | return; 86 | } 87 | String clipboardText = cs.toString(); 88 | convHelper(true, true, clipboardText); 89 | } 90 | } 91 | } 92 | 93 | private void convHelper (boolean readonly, boolean quitAfterConv, CharSequence text) { 94 | SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); 95 | boolean easyMode = pref.getBoolean(Constant.PREF_SETTINGS_EASY_MODE, true); 96 | boolean autodetect = pref.getBoolean(Constant.PREF_SETTINGS_AUTODETECT_MODE, true); 97 | int tradMode = Integer.parseInt(pref.getString(Constant.PREF_SETTINGS_TRAD_MODE, "0")); 98 | int simpMode = Integer.parseInt(pref.getString(Constant.PREF_SETTINGS_SIMP_MODE, "0")); 99 | 100 | if (text == null) { 101 | Toast toast = Toast.makeText(getApplicationContext(), R.string.menu_toast_unknown_error, Toast.LENGTH_SHORT); 102 | toast.show(); 103 | } else { 104 | if (easyMode) { 105 | // easy mode: if detected trad/simp, convert with OpenCC; if not, ask for trad/simp then convert with OpenCC 106 | ChineseTypes type = SimpleConvert.checkString(text.toString(), getApplicationContext()); 107 | if (type == ChineseTypes.TRADITIONAL_CHINESE) { 108 | convAndSet(Constant.T2S, text, readonly, quitAfterConv); 109 | } else if (type == ChineseTypes.SIMPLIFIED_CHINESE) { 110 | convAndSet(Constant.S2T, text, readonly, quitAfterConv); 111 | } else { 112 | tradSimpPopup(Constant.T2S, Constant.S2T, text, readonly, quitAfterConv); 113 | } 114 | } else if (autodetect) { 115 | // auto detect mode: if detected trad/simp, convert directly if conversion preference chosen, 116 | // otherwise present all trad or all simp options 117 | // if not detected, ask if text is simp or trad if conversion preference chosen, 118 | // otherwise present every possible options 119 | ChineseTypes type = SimpleConvert.checkString(text.toString(), getApplicationContext()); 120 | if (type == ChineseTypes.TRADITIONAL_CHINESE) { 121 | if (tradMode == 0) { 122 | tradPopup(text, readonly, quitAfterConv); 123 | } else { 124 | convAndSet(tradMode, text, readonly, quitAfterConv); 125 | } 126 | } else if (type == ChineseTypes.SIMPLIFIED_CHINESE) { 127 | if (simpMode == 0) { 128 | simpPopup(text, readonly, quitAfterConv); 129 | } else { 130 | convAndSet(simpMode, text, readonly, quitAfterConv); 131 | } 132 | } else { 133 | if (tradMode == 0 || simpMode == 0) { 134 | allOptionsPopup(text, readonly, quitAfterConv); 135 | } else { 136 | tradSimpPopup(tradMode, simpMode, text, readonly, quitAfterConv); 137 | } 138 | } 139 | } 140 | else { 141 | // fully manual mode: show all options 142 | allOptionsPopup(text, readonly, quitAfterConv); 143 | } 144 | } 145 | } 146 | 147 | private void tradSimpPopup (int tradType, int simpType, CharSequence text, boolean readonly, boolean quitAfterConv) { 148 | // popup when user selection of traditional or simplified text is required 149 | setContentView(R.layout.activity_convert_simple); 150 | popupSetCancelListener(quitAfterConv); 151 | 152 | Button conv_button = findViewById(R.id.button4); 153 | conv_button.setOnClickListener(v -> { 154 | RadioGroup rgPopup = findViewById(R.id.radioGroup); 155 | int id = rgPopup.getCheckedRadioButtonId(); 156 | int sel; 157 | if (id == R.id.popupRadioType1) { 158 | sel = tradType; // traditional 159 | } else if (id == R.id.popupRadioType2) { 160 | sel = simpType; // simplified 161 | } else { 162 | sel = 0; 163 | } 164 | convAndSet(sel, text, readonly, quitAfterConv); 165 | }); 166 | } 167 | 168 | private void tradPopup (CharSequence text, boolean readonly, boolean quitAfterConv) { 169 | // popup with only traditional options 170 | setContentView(R.layout.activity_convert_trad); 171 | popupSetCancelListener(quitAfterConv); 172 | 173 | Button conv_button = findViewById(R.id.button4); 174 | conv_button.setOnClickListener(v -> { 175 | RadioGroup rgPopup = findViewById(R.id.radioGroup); 176 | int id = rgPopup.getCheckedRadioButtonId(); 177 | int sel; 178 | if (id == R.id.popupRadioType5) { 179 | sel = Constant.T2S; 180 | } else if (id == R.id.popupRadioType6) { 181 | sel = Constant.TW2S; 182 | } else if (id == R.id.popupRadioType7) { 183 | sel = Constant.HK2S; 184 | } else if (id == R.id.popupRadioType8) { 185 | sel = Constant.TW2SP; 186 | } else if (id == R.id.popupRadioType9) { 187 | sel = Constant.T2TW; 188 | } else if (id == R.id.popupRadioType10) { 189 | sel = Constant.T2HK; 190 | } else { 191 | sel = 0; 192 | } 193 | convAndSet(sel, text, readonly, quitAfterConv); 194 | }); 195 | } 196 | 197 | private void simpPopup (CharSequence text, boolean readonly, boolean quitAfterConv) { 198 | // popup with only simplified options 199 | setContentView(R.layout.activity_convert_simp); 200 | popupSetCancelListener(quitAfterConv); 201 | 202 | Button conv_button = findViewById(R.id.button4); 203 | conv_button.setOnClickListener(v -> { 204 | RadioGroup rgPopup = findViewById(R.id.radioGroup); 205 | int id = rgPopup.getCheckedRadioButtonId(); 206 | int sel; 207 | if (id == R.id.popupRadioType1) { 208 | sel = Constant.S2T; 209 | } else if (id == R.id.popupRadioType2) { 210 | sel = Constant.S2TW; 211 | } else if (id == R.id.popupRadioType3) { 212 | sel = Constant.S2HK; 213 | } else if (id == R.id.popupRadioType4) { 214 | sel = Constant.S2TWP; 215 | } else { 216 | sel = 0; 217 | } 218 | convAndSet(sel, text, readonly, quitAfterConv); 219 | }); 220 | } 221 | 222 | private void allOptionsPopup (CharSequence text, boolean readonly, boolean quitAfterConv) { 223 | // popup with every single possible options 224 | setContentView(R.layout.activity_convert); 225 | popupSetCancelListener(quitAfterConv); 226 | 227 | Button conv_button = findViewById(R.id.button4); 228 | conv_button.setOnClickListener(v -> { 229 | int sel; 230 | RadioGroup rgPopup = findViewById(R.id.radioGroup); 231 | int id = rgPopup.getCheckedRadioButtonId(); 232 | if (id == R.id.popupRadioType1) { 233 | sel = Constant.S2T; 234 | } else if (id == R.id.popupRadioType2) { 235 | sel = Constant.S2TW; 236 | } else if (id == R.id.popupRadioType3) { 237 | sel = Constant.S2HK; 238 | } else if (id == R.id.popupRadioType4) { 239 | sel = Constant.S2TWP; 240 | } else if (id == R.id.popupRadioType5) { 241 | sel = Constant.T2S; 242 | } else if (id == R.id.popupRadioType6) { 243 | sel = Constant.TW2S; 244 | } else if (id == R.id.popupRadioType7) { 245 | sel = Constant.HK2S; 246 | } else if (id == R.id.popupRadioType8) { 247 | sel = Constant.TW2SP; 248 | } else if (id == R.id.popupRadioType9) { 249 | sel = Constant.T2TW; 250 | } else if (id == R.id.popupRadioType10) { 251 | sel = Constant.T2HK; 252 | } else { 253 | sel = 0; 254 | } 255 | convAndSet(sel, text, readonly, quitAfterConv); 256 | }); 257 | } 258 | 259 | private void showConversionError () { 260 | Toast toast = Toast.makeText(getApplicationContext(), getString(R.string.menu_autonotdetected), Toast.LENGTH_SHORT); 261 | toast.show(); 262 | } 263 | 264 | private void popupSetCancelListener (boolean quitAfterConv) { 265 | Button cancel_button = findViewById(R.id.button5); 266 | cancel_button.setOnClickListener(v -> { 267 | finish(); 268 | if (quitAfterConv) { 269 | moveTaskToBack(true); 270 | } 271 | }); 272 | } 273 | 274 | private void convAndSet(int sel, CharSequence text, boolean readonly, boolean quitAfterConv) { 275 | if (sel != 0) { 276 | String fromText = Objects.requireNonNull(text).toString(); 277 | String resultText = ConvertUtils.openCCConv(fromText, sel, getApplicationContext()); 278 | 279 | if (readonly) { 280 | // copy converted to clipboard 281 | ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); 282 | ClipData clip = ClipData.newPlainText(Constant.CLIPBOARD_LABEL, resultText); 283 | clipboard.setPrimaryClip(clip); 284 | Toast toast = Toast.makeText(getApplicationContext(), R.string.menu_readonly, Toast.LENGTH_SHORT); 285 | toast.show(); 286 | } else { 287 | // replace text directly 288 | Intent intent = new Intent(); 289 | intent.putExtra(Intent.EXTRA_PROCESS_TEXT, resultText); 290 | setResult(RESULT_OK, intent); 291 | } 292 | } else { 293 | showConversionError(); 294 | } 295 | finish(); 296 | if (quitAfterConv) { 297 | moveTaskToBack(true); 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /app/src/main/java/com/iatfei/tsconverter/ConvertUtils.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Fei Kuan. 3 | * 4 | * This file is part of Chinese Converter 5 | * (see ). 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | package com.iatfei.tsconverter; 22 | 23 | import android.content.Context; 24 | import android.content.SharedPreferences; 25 | 26 | import com.zqc.opencc.android.lib.ChineseConverter; 27 | import com.zqc.opencc.android.lib.ConversionType; 28 | 29 | import static com.zqc.opencc.android.lib.ConversionType.*; 30 | 31 | import androidx.preference.PreferenceManager; 32 | 33 | import java.util.HashSet; 34 | import java.util.Set; 35 | 36 | class ConvertUtils { 37 | 38 | static String openCCConv(String from, int type, Context context) { 39 | ConversionType convType = findType (type); 40 | if (convType == null) { 41 | return from; 42 | } 43 | SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(context); 44 | if (pref.getBoolean(Constant.PREF_SETTINGS_TEXT_PROCESSING, false)) { 45 | Set delSel = pref.getStringSet(Constant.PREF_SETTINGS_DELETE_TEXT, new HashSet<>()); 46 | from = textProcessing(from, delSel); 47 | } 48 | return ChineseConverter.convert(from, convType, context); 49 | } 50 | 51 | private static String textProcessing(String from, Set del) { 52 | if (del.contains("space")) { 53 | from = from.replaceAll("\\p{Z}+", ""); 54 | } 55 | if (del.contains("numbers")) { 56 | from = from.replaceAll("\\d", ""); 57 | } 58 | if (del.contains("latin")) { 59 | from = from.replaceAll("\\p{Script=Latin}+", ""); 60 | } 61 | if (del.contains("specialChar")) { 62 | from = from.replaceAll("\\p{S}", ""); 63 | } 64 | if (del.contains("emptyLine")) { 65 | from = from.replaceAll("(\\r?\\n){2,}", "\n"); 66 | } 67 | if (del.contains("hangul")) { 68 | from = from.replaceAll("\\p{Script=Hangul}+", ""); 69 | } 70 | if (del.contains("hirakata")) { 71 | from = from.replaceAll("[\\p{IsKatakana}\\p{IsHiragana}]+", ""); 72 | } 73 | if (del.contains("bopomofo")) { 74 | from = from.replaceAll("\\p{Script=Bopomofo}+", ""); 75 | } 76 | if (del.contains("punctuation")) { 77 | from = from.replaceAll("\\p{P}+", ""); 78 | } 79 | return from; 80 | } 81 | 82 | private static ConversionType findType(int type){ 83 | return switch (type) { 84 | case Constant.S2T -> S2T; 85 | case Constant.S2TW -> S2TW; 86 | case Constant.S2HK -> S2HK; 87 | case Constant.S2TWP -> S2TWP; 88 | case Constant.T2S -> T2S; 89 | case Constant.TW2S -> TW2S; 90 | case Constant.HK2S -> HK2S; 91 | case Constant.TW2SP -> TW2SP; 92 | case Constant.T2TW -> T2TW; 93 | case Constant.T2HK -> T2HK; 94 | default -> null; 95 | }; 96 | } 97 | 98 | static int radioToType (boolean t1, boolean t2, boolean t3, boolean v1, boolean v2, boolean v3, boolean v4, boolean v5, boolean i1, boolean i2, boolean i3){ 99 | if (i1 && v1 && t1) 100 | return Constant.S2T; 101 | if (i1 && v2 && t1) 102 | return Constant.S2TW; 103 | if (i1 && v3 && t1) 104 | return Constant.S2HK; 105 | if (i2 && v2 && t1) 106 | return Constant.S2TWP; 107 | if (i1 && v1 && t2) 108 | return Constant.T2S; 109 | if (i1 && v4 && t2) 110 | return Constant.TW2S; 111 | if (i1 && v5 && t2) 112 | return Constant.HK2S; 113 | if (i3 && v4 && t2) 114 | return Constant.TW2SP; 115 | if (i1 && v2 && t3) 116 | return Constant.T2TW; 117 | if (i1 && v3 && t3) 118 | return Constant.T2HK; 119 | return -1; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/com/iatfei/tsconverter/FirstStartupActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Fei Kuan. 3 | * 4 | * This file is part of Chinese Converter 5 | * (see ). 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | package com.iatfei.tsconverter 22 | 23 | import android.os.Bundle 24 | import androidx.fragment.app.Fragment 25 | import com.github.appintro.AppIntro 26 | import com.github.appintro.AppIntroFragment 27 | import androidx.core.content.edit 28 | 29 | class FirstStartupActivity : AppIntro() { 30 | override fun onCreate(savedInstanceState: Bundle?) { 31 | super.onCreate(savedInstanceState) 32 | isColorTransitionsEnabled = true 33 | setImmersiveMode() 34 | 35 | addSlide(AppIntroFragment.createInstance( 36 | title = getString(R.string.appintro_title_1), 37 | description = getString(R.string.appintro_content_1), 38 | imageDrawable = R.drawable.ic_applogo_intro, 39 | backgroundColorRes = R.color.appIntroOne 40 | )) 41 | addSlide(AppIntroFragment.createInstance( 42 | title = getString(R.string.appintro_title_2), 43 | description = getString(R.string.appintro_content_2), 44 | imageDrawable = R.drawable.ic_baseline_info_24, 45 | backgroundColorRes = R.color.appIntroTwo 46 | )) 47 | addSlide(AppIntroFragment.createInstance( 48 | title = getString(R.string.appintro_title_3_1), 49 | description = getString(R.string.appintro_content_3_1), 50 | imageDrawable = R.drawable.tutorial_textselection_1, 51 | backgroundColorRes = R.color.appIntroFour 52 | )) 53 | addSlide(AppIntroFragment.createInstance( 54 | title = getString(R.string.appintro_title_3_2), 55 | description = getString(R.string.appintro_content_3_2), 56 | imageDrawable = R.drawable.tutorial_textselection_2, 57 | backgroundColorRes = R.color.appIntroFour 58 | )) 59 | addSlide(AppIntroFragment.createInstance( 60 | title = getString(R.string.appintro_title_3_3), 61 | description = getString(R.string.appintro_content_3_3), 62 | imageDrawable = R.drawable.tutorial_textselection_3, 63 | backgroundColorRes = R.color.appIntroFour 64 | )) 65 | addSlide(AppIntroFragment.createInstance( 66 | title = getString(R.string.appintro_title_4), 67 | description = getString(R.string.appintro_content_4), 68 | imageDrawable = R.drawable.ic_applogo_intro, 69 | backgroundColorRes = R.color.appIntroThree 70 | )) 71 | } 72 | 73 | override fun onSkipPressed(currentFragment: Fragment?) { 74 | super.onSkipPressed(currentFragment) 75 | 76 | val prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(baseContext) 77 | prefs.edit { 78 | putInt(Constant.PREF_PREVIOUS_STARTED_VERSION, BuildConfig.VERSION_CODE) 79 | } 80 | finish() 81 | } 82 | 83 | override fun onDonePressed(currentFragment: Fragment?) { 84 | super.onDonePressed(currentFragment) 85 | 86 | val prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(baseContext) 87 | prefs.edit { 88 | putInt(Constant.PREF_PREVIOUS_STARTED_VERSION, BuildConfig.VERSION_CODE) 89 | } 90 | finish() 91 | } 92 | } -------------------------------------------------------------------------------- /app/src/main/java/com/iatfei/tsconverter/OpenSourceActivity.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Fei Kuan. 3 | * 4 | * This file is part of Chinese Converter 5 | * (see ). 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | package com.iatfei.tsconverter 22 | 23 | import android.os.Bundle 24 | import androidx.appcompat.app.AppCompatActivity 25 | import com.google.android.material.appbar.MaterialToolbar 26 | import com.mikepenz.aboutlibraries.LibsBuilder 27 | 28 | class OpenSourceActivity : AppCompatActivity() { 29 | 30 | override fun onCreate(savedInstanceState: Bundle?) { 31 | super.onCreate(savedInstanceState) 32 | setContentView(R.layout.activity_open_source) 33 | 34 | val toolbar = findViewById(R.id.toolbar_opensource) 35 | setSupportActionBar(toolbar) 36 | val actionBar = supportActionBar 37 | actionBar?.setDisplayHomeAsUpEnabled(true) 38 | 39 | val fragment = LibsBuilder() 40 | .withAboutIconShown(true) 41 | .withAboutVersionShown(true) 42 | .withLicenseShown(true) 43 | .withAboutAppName(getString(R.string.app_name)) 44 | .withAboutDescription("I can't believe you clicked on this! Thank you for using my app!
Special thanks to BYVoid for developing OpenCC.

願世界和平。") 45 | .supportFragment() 46 | 47 | supportFragmentManager.beginTransaction() 48 | .replace(R.id.container, fragment) 49 | .commit() 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/iatfei/tsconverter/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Fei Kuan. 3 | * 4 | * This file is part of Chinese Converter 5 | * (see ). 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | package com.iatfei.tsconverter; 22 | 23 | import android.content.SharedPreferences; 24 | import android.os.Bundle; 25 | 26 | import androidx.appcompat.app.ActionBar; 27 | import androidx.appcompat.app.AppCompatActivity; 28 | import com.google.android.material.appbar.MaterialToolbar; 29 | 30 | import androidx.appcompat.app.AppCompatDelegate; 31 | import androidx.core.os.LocaleListCompat; 32 | import androidx.preference.ListPreference; 33 | import androidx.preference.MultiSelectListPreference; 34 | import androidx.preference.PreferenceFragmentCompat; 35 | import androidx.preference.PreferenceManager; 36 | import androidx.preference.SwitchPreferenceCompat; 37 | 38 | import java.util.Arrays; 39 | import java.util.List; 40 | import java.util.Locale; 41 | 42 | public class SettingsActivity extends AppCompatActivity { 43 | 44 | @Override 45 | protected void onCreate(Bundle savedInstanceState) { 46 | super.onCreate(savedInstanceState); 47 | setContentView(R.layout.activity_settings); 48 | if (savedInstanceState == null) { 49 | getSupportFragmentManager() 50 | .beginTransaction() 51 | .replace(R.id.settings, new SettingsFragment()) 52 | .commit(); 53 | } 54 | MaterialToolbar toolbar = findViewById(R.id.toolbar_settings); 55 | setSupportActionBar(toolbar); 56 | ActionBar actionBar = getSupportActionBar(); 57 | if (actionBar != null) { 58 | actionBar.setDisplayHomeAsUpEnabled(true); 59 | } 60 | } 61 | 62 | public static class SettingsFragment extends PreferenceFragmentCompat { 63 | @Override 64 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 65 | // todo make popups material 3 66 | addPreferencesFromResource(R.xml.settings); 67 | 68 | // finding preferences and setting initial values 69 | final SharedPreferences pref = PreferenceManager.getDefaultSharedPreferences(requireContext()); 70 | final SwitchPreferenceCompat simpleSwitch = findPreference(Constant.PREF_SETTINGS_EASY_MODE); 71 | final SwitchPreferenceCompat autodetectSwitch = findPreference(Constant.PREF_SETTINGS_AUTODETECT_MODE); 72 | final SwitchPreferenceCompat textProcessingSwitch = findPreference("settings_text_processing_master_switch"); 73 | final ListPreference lpTraditional = findPreference(Constant.PREF_SETTINGS_TRAD_MODE); 74 | final ListPreference lpSimplified = findPreference(Constant.PREF_SETTINGS_SIMP_MODE); 75 | final MultiSelectListPreference deleteTextPref = findPreference("multi_select_list_preference_1"); 76 | 77 | if (autodetectSwitch != null && lpTraditional != null && lpSimplified != null 78 | && simpleSwitch != null && textProcessingSwitch != null && deleteTextPref != null) { 79 | boolean easyMode = pref.getBoolean(Constant.PREF_SETTINGS_EASY_MODE, true); 80 | boolean autoDetect = pref.getBoolean(Constant.PREF_SETTINGS_AUTODETECT_MODE, true); 81 | boolean textProcessing = pref.getBoolean(Constant.PREF_SETTINGS_TEXT_PROCESSING, false); 82 | autodetectSwitch.setEnabled(!easyMode); 83 | lpTraditional.setEnabled(!easyMode && autoDetect); 84 | lpSimplified.setEnabled(!easyMode && autoDetect); 85 | deleteTextPref.setEnabled(textProcessing); 86 | simpleSwitch.setOnPreferenceChangeListener((preference, newValue) -> { 87 | boolean autoDetect2 = pref.getBoolean(Constant.PREF_SETTINGS_AUTODETECT_MODE, true); 88 | boolean newValEasy = (Boolean) newValue; 89 | autodetectSwitch.setEnabled(!newValEasy); 90 | lpTraditional.setEnabled(!newValEasy && autoDetect2); 91 | lpSimplified.setEnabled(!newValEasy && autoDetect2); 92 | return true; 93 | }); 94 | autodetectSwitch.setOnPreferenceChangeListener((preference, newValue) -> { 95 | boolean newVal = (Boolean) newValue; 96 | lpTraditional.setEnabled(newVal); 97 | lpSimplified.setEnabled(newVal); 98 | return true; 99 | }); 100 | textProcessingSwitch.setOnPreferenceChangeListener((preference, newValue) -> { 101 | boolean newVal = (Boolean) newValue; 102 | deleteTextPref.setEnabled(newVal); 103 | return true; 104 | }); 105 | } 106 | ListPreference langSelectorPref = findPreference(Constant.PREF_SETTINGS_UI_LANGUAGE); 107 | LocaleListCompat selectedLocaleList = AppCompatDelegate.getApplicationLocales(); 108 | List supportedLocaleIDs = Arrays.asList(getResources().getStringArray(R.array.ui_lang_settings_languages_vals)); 109 | List supportedLocale = Arrays.asList(getResources().getStringArray(R.array.ui_lang_settings_languages)); 110 | if (langSelectorPref != null ) { 111 | if (selectedLocaleList.isEmpty()) { 112 | // no language override selected 113 | langSelectorPref.setSummary(supportedLocale.get(0)); 114 | langSelectorPref.setValueIndex(0); 115 | } else { 116 | Locale selectedLocale = selectedLocaleList.get(0); 117 | String selectedLocaleString = selectedLocale != null ? selectedLocale.toLanguageTag() : ""; 118 | if (selectedLocaleString.equals("und")) { 119 | // no language override selected, happens on Android 6? 120 | langSelectorPref.setSummary(supportedLocale.get(0)); 121 | langSelectorPref.setValueIndex(0); 122 | } else if (supportedLocaleIDs.contains(selectedLocaleString)) { 123 | // language override selected. matches an item in the preference language list 124 | langSelectorPref.setValue(selectedLocaleString); 125 | langSelectorPref.setSummary(getResources() 126 | .getStringArray(R.array.ui_lang_settings_languages) 127 | [supportedLocaleIDs.indexOf(selectedLocaleString)]); 128 | } else { 129 | // language override selected. does not match an item in our list (probably selected from system settings) 130 | langSelectorPref.setValue(null); 131 | langSelectorPref.setSummary(selectedLocaleString); 132 | } 133 | } 134 | langSelectorPref.setOnPreferenceChangeListener((preference, newValue) -> { 135 | LocaleListCompat newLocale = LocaleListCompat.forLanguageTags(newValue.toString()); 136 | AppCompatDelegate.setApplicationLocales(newLocale); 137 | return true; 138 | }); 139 | } 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /app/src/main/java/com/iatfei/tsconverter/SimpleConvert.kt: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2020-2023 Fei Kuan. 3 | * 4 | * This file is part of Chinese Converter 5 | * (see ). 6 | * 7 | * This program is free software: you can redistribute it and/or modify 8 | * it under the terms of the GNU General Public License as published by 9 | * the Free Software Foundation, either version 3 of the License, or 10 | * (at your option) any later version. 11 | * 12 | * This program is distributed in the hope that it will be useful, 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | * GNU General Public License for more details. 16 | * 17 | * You should have received a copy of the GNU General Public License 18 | * along with this program. If not, see . 19 | */ 20 | 21 | /* 22 | * Special thanks to Renn on GitHub who contributed to this piece of code. 23 | * Copyright (c) 2021 Renn. Released under GNU GPL v3+. 24 | */ 25 | 26 | package com.iatfei.tsconverter 27 | 28 | import android.content.Context 29 | import android.os.Build 30 | import java.io.ObjectInputStream 31 | 32 | object SimpleConvert { 33 | private var charMap = HashMap() 34 | 35 | private fun loadMap(c: Context) { 36 | val objectInput = ObjectInputStream(c.resources.openRawResource(R.raw.charmap)) 37 | val readFile = objectInput.readObject() 38 | charMap = readFile as HashMap 39 | objectInput.close() 40 | } 41 | 42 | @JvmStatic 43 | fun checkString(str: String, c: Context): ChineseTypes { 44 | // Idea: find the first unique Simplified/Traditional character (i.e. that character only exists in Traditional/Simplified) 45 | // Approach: The HashMap contains all Unicode code points of unique Trad/Simp characters and whether it's Trad or Simp, 46 | // just search through the map. 47 | if (charMap.size < 2) { 48 | // if charMap is empty, load it 49 | loadMap(c) 50 | } 51 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 52 | val codePoints = StringBuilder(str).codePoints() 53 | for (codePoint in codePoints) { 54 | val tempResult = charMap[codePoint] 55 | if (tempResult != null) { 56 | return tempResult 57 | } 58 | } 59 | } else { 60 | // old approach which does not support surrogates (beyond BMP) 61 | for (i in str.indices) { 62 | val tempCodePoint = Character.codePointAt(str, i) 63 | if (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF) { 64 | val tempResult = charMap[tempCodePoint] 65 | if (tempResult != null) { 66 | return tempResult 67 | } 68 | } 69 | } 70 | } 71 | return ChineseTypes.NONE 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tutorial_textselection_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-hdpi/tutorial_textselection_1.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tutorial_textselection_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-hdpi/tutorial_textselection_2.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/tutorial_textselection_3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-hdpi/tutorial_textselection_3.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tutorial_textselection_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-mdpi/tutorial_textselection_1.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tutorial_textselection_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-mdpi/tutorial_textselection_2.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/tutorial_textselection_3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-mdpi/tutorial_textselection_3.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/tutorial_textselection_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-xhdpi/tutorial_textselection_1.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/tutorial_textselection_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-xhdpi/tutorial_textselection_2.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/tutorial_textselection_3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-xhdpi/tutorial_textselection_3.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/tutorial_textselection_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-xxhdpi/tutorial_textselection_1.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/tutorial_textselection_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-xxhdpi/tutorial_textselection_2.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/tutorial_textselection_3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-xxhdpi/tutorial_textselection_3.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/tutorial_textselection_1.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-xxxhdpi/tutorial_textselection_1.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/tutorial_textselection_2.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-xxxhdpi/tutorial_textselection_2.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/tutorial_textselection_3.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fei0316/OpenCC-android-app/35e21ab684fd5a40ce8bfe907a01cc92c24acf88/app/src/main/res/drawable-xxxhdpi/tutorial_textselection_3.webp -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_applogo_intro.xml: -------------------------------------------------------------------------------- 1 | 20 | 21 | 26 | 30 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_info_24.xml: -------------------------------------------------------------------------------- 1 | 20 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_baseline_refresh_24.xml: -------------------------------------------------------------------------------- 1 | 20 | 21 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 27 | 30 | 31 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 20 | 21 | 26 | 30 | 32 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout-land/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 26 | 27 | 31 | 32 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 20 | 21 | 25 | 26 | 30 | 31 | 36 | 37 | 38 | 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_convert.xml: -------------------------------------------------------------------------------- 1 | 2 | 21 | 22 | 30 | 31 | 35 | 36 | 39 | 40 | 50 | 51 | 56 | 57 | 63 | 64 | 69 | 70 | 75 | 76 | 81 | 82 | 88 | 89 | 94 | 95 | 100 | 101 | 106 | 107 | 112 | 113 | 119 | 120 | 125 | 126 | 131 | 132 | 133 | 134 | 140 | 141 |