├── .gitattributes
├── .github
└── workflows
│ ├── dependencies.yml
│ └── release.yml
├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── cn
│ │ └── umafan
│ │ └── lib
│ │ └── android
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── assets
│ │ ├── db
│ │ │ └── main.db
│ │ └── reader
│ │ │ ├── css
│ │ │ ├── element-ui.css
│ │ │ ├── quill.core.css
│ │ │ ├── quill.snow.css
│ │ │ └── umalib.reader.css
│ │ │ ├── index.html
│ │ │ └── js
│ │ │ ├── element-ui.js
│ │ │ ├── index.js
│ │ │ └── vue.js
│ ├── java
│ │ └── cn
│ │ │ └── umafan
│ │ │ └── lib
│ │ │ └── android
│ │ │ ├── model
│ │ │ ├── DataBaseHandler.kt
│ │ │ ├── MyApplication.kt
│ │ │ ├── MyBaseActivity.kt
│ │ │ ├── MyBaseViewModel.kt
│ │ │ ├── PageSelectorViewModel.kt
│ │ │ ├── SearchBean.kt
│ │ │ └── db
│ │ │ │ ├── ArtCreator.java
│ │ │ │ ├── ArtInfo.java
│ │ │ │ ├── Article.java
│ │ │ │ ├── Creator.java
│ │ │ │ ├── Dict.java
│ │ │ │ ├── Rec.java
│ │ │ │ ├── Tag.java
│ │ │ │ └── Tagged.java
│ │ │ ├── ui
│ │ │ ├── MainIntroActivity.kt
│ │ │ ├── UpdateLogActivity.kt
│ │ │ ├── favorites
│ │ │ │ ├── FavoritesFragment.kt
│ │ │ │ └── FavoritesViewModel.kt
│ │ │ ├── history
│ │ │ │ ├── HistoryFragment.kt
│ │ │ │ └── HistoryViewModel.kt
│ │ │ ├── home
│ │ │ │ ├── HomeFragment.kt
│ │ │ │ ├── HomeViewModel.kt
│ │ │ │ └── model
│ │ │ │ │ ├── ArticleInfoItem.kt
│ │ │ │ │ └── PageItem.kt
│ │ │ ├── main
│ │ │ │ ├── DatabaseCopyThread.kt
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── MainViewModel.kt
│ │ │ │ └── model
│ │ │ │ │ ├── CreatorSuggestionAdapter.kt
│ │ │ │ │ ├── TagSelectedItem.kt
│ │ │ │ │ └── TagSuggestionAdapter.kt
│ │ │ ├── reader
│ │ │ │ ├── ReaderActivity.kt
│ │ │ │ ├── ReaderViewModel.kt
│ │ │ │ └── model
│ │ │ │ │ └── ReaderJSInterface.kt
│ │ │ ├── recommend
│ │ │ │ ├── RecommendFragment.kt
│ │ │ │ ├── RecommendViewModel.kt
│ │ │ │ └── model
│ │ │ │ │ ├── RecCommentItem.kt
│ │ │ │ │ ├── RecInfo.kt
│ │ │ │ │ ├── RecJumpItem.kt
│ │ │ │ │ └── RecTabItem.kt
│ │ │ ├── setting
│ │ │ │ ├── SettingActivity.kt
│ │ │ │ └── SettingViewModel.kt
│ │ │ └── thanks
│ │ │ │ ├── ThanksFragment.kt
│ │ │ │ └── ThanksViewModel.kt
│ │ │ └── util
│ │ │ ├── ContentUriUtil.java
│ │ │ ├── DownloadUtil.kt
│ │ │ ├── FavoriteArticleUtil.kt
│ │ │ ├── HistoryUtil.kt
│ │ │ ├── PageSizeUtil.kt
│ │ │ ├── PinyinUtil.java
│ │ │ ├── ReaderSettingUtil.kt
│ │ │ ├── SettingUtil.kt
│ │ │ ├── ZipUtil.kt
│ │ │ └── network
│ │ │ ├── ServiceCreator.kt
│ │ │ ├── UpdateUtil.kt
│ │ │ ├── model
│ │ │ ├── UpdateBean.kt
│ │ │ ├── UpdateButtonBean.kt
│ │ │ └── UpdateInfoBean.kt
│ │ │ └── service
│ │ │ └── UpdateService.kt
│ └── res
│ │ ├── drawable-v24
│ │ ├── ic_github.xml
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── app_bg.png
│ │ ├── app_database_intro.png
│ │ ├── app_func_intro.jpg
│ │ ├── app_tag_intro.jpg
│ │ ├── app_view_intro.jpg
│ │ ├── baseline_arrow_right_24.xml
│ │ ├── baseline_help_outline_24.xml
│ │ ├── baseline_menu_24.xml
│ │ ├── baseline_menu_open_24.xml
│ │ ├── baseline_volunteer_activism_24.xml
│ │ ├── ic_baseline_article_24.xml
│ │ ├── ic_baseline_cancel_24.xml
│ │ ├── ic_baseline_chat_24.xml
│ │ ├── ic_baseline_cloud_download_24.xml
│ │ ├── ic_baseline_cloud_upload_24.xml
│ │ ├── ic_baseline_collections_bookmark_24.xml
│ │ ├── ic_baseline_color_lens_24.xml
│ │ ├── ic_baseline_filter_alt_24.xml
│ │ ├── ic_baseline_history_24.xml
│ │ ├── ic_baseline_insert_photo_24.xml
│ │ ├── ic_baseline_navigate_before_24.xml
│ │ ├── ic_baseline_navigate_next_24.xml
│ │ ├── ic_baseline_recommend_24.xml
│ │ ├── ic_baseline_refresh_24.xml
│ │ ├── ic_baseline_search_24.xml
│ │ ├── ic_baseline_settings_24.xml
│ │ ├── ic_baseline_star_24.xml
│ │ ├── ic_baseline_star_outline_24.xml
│ │ ├── ic_baseline_system_update_24.xml
│ │ ├── ic_launcher.png
│ │ ├── ic_launcher_background.xml
│ │ ├── ic_lightbulb_24px.xml
│ │ ├── index_bg.png
│ │ ├── kitasan.jpg
│ │ ├── normal_bg.png
│ │ ├── side_nav_bar.xml
│ │ └── tokaiteio.png
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ ├── activity_main_intro.xml
│ │ ├── activity_reader.xml
│ │ ├── activity_setting.xml
│ │ ├── activity_update_log.xml
│ │ ├── app_bar_main.xml
│ │ ├── content_main.xml
│ │ ├── dialog_loading_database.xml
│ │ ├── dialog_page_selector.xml
│ │ ├── dialog_reader_setting.xml
│ │ ├── dialog_search_filter.xml
│ │ ├── fragment_collections.xml
│ │ ├── fragment_favorites.xml
│ │ ├── fragment_histroy.xml
│ │ ├── fragment_home.xml
│ │ ├── fragment_recommend.xml
│ │ ├── fragment_thanks.xml
│ │ ├── item_article_card.xml
│ │ ├── item_jump_button.xml
│ │ ├── item_page_button.xml
│ │ ├── item_rec_card.xml
│ │ ├── item_rec_comment.xml
│ │ ├── item_selected_tag.xml
│ │ ├── item_tag_suggestion.xml
│ │ └── nav_header_main.xml
│ │ ├── menu
│ │ ├── activity_main_drawer.xml
│ │ └── main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── navigation
│ │ └── mobile_navigation.xml
│ │ ├── values-land
│ │ └── dimens.xml
│ │ ├── values-night
│ │ └── themes.xml
│ │ ├── values-w1240dp
│ │ └── dimens.xml
│ │ ├── values-w600dp
│ │ └── dimens.xml
│ │ ├── values
│ │ ├── attr.xml
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── shape.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ │ └── xml
│ │ └── file_paths.xml
│ └── test
│ └── java
│ └── cn
│ └── umafan
│ └── lib
│ └── android
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── preference
├── .gitignore
├── build.gradle
├── consumer-rules.pro
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── liangguo
│ │ └── preference
│ │ └── views
│ │ ├── BaselineGridTextView.java
│ │ └── CommonSettingView.kt
│ └── res
│ ├── drawable
│ ├── ic_baseline_keyboard_arrow_right_24.xml
│ ├── switch_thumb.xml
│ └── switch_track.xml
│ ├── layout
│ └── item_common_preference.xml
│ └── values
│ ├── attrs_baseline_textview.xml
│ ├── attrs_common_setting_view.xml
│ ├── attrs_pref_common.xml
│ ├── attrs_pref_list.xml
│ ├── dimens.xml
│ └── strings.xml
└── settings.gradle
/.gitattributes:
--------------------------------------------------------------------------------
1 | *.db filter=lfs diff=lfs merge=lfs -text
2 |
--------------------------------------------------------------------------------
/.github/workflows/dependencies.yml:
--------------------------------------------------------------------------------
1 | name: gradle-dependency-detection
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | gradle-dependency-detection:
10 | runs-on: ubuntu-latest
11 | # The Dependency Submission API requires write permission
12 | permissions:
13 | contents: write
14 | steps:
15 | - uses: actions/checkout@v3
16 | - name: Analyze dependencies
17 | uses: mikepenz/gradle-dependency-submission@v0.8.4
18 | with:
19 | gradle-build-module: |-
20 | :app
21 | :preference
22 | gradle-build-configuration: |-
23 | debugCompileClasspath
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | name: umalib-build-and-release
2 |
3 | on:
4 | push:
5 | tags:
6 | - "v*"
7 |
8 | jobs:
9 | build-and-release:
10 | runs-on: ubuntu-latest
11 | permissions:
12 | contents: write
13 | steps:
14 | - uses: actions/checkout@v4
15 | with:
16 | lfs: 'true'
17 | - name: set up JDK 11
18 | uses: actions/setup-java@v3
19 | with:
20 | java-version: '11'
21 | distribution: 'temurin'
22 | cache: gradle
23 | - name: Grant execute permission for gradlew
24 | run: chmod +x gradlew
25 | - name: Assemble release
26 | run: ./gradlew :app:assembleRelease
27 | - name: ZipAlign & Sign android release
28 | id: sign_app
29 | uses: kevin-david/zipalign-sign-android-release@v1.1
30 | with:
31 | releaseDirectory: app/build/outputs/apk/release
32 | signingKeyBase64: ${{ secrets.JKS_FILE }}
33 | alias: ${{ secrets.KEY_ALIAS }}
34 | keyStorePassword: ${{ secrets.STORE_PASSWORD }}
35 | keyPassword: ${{ secrets.KEY_PASSWORD }}
36 | zipAlign: true
37 | - name: Move
38 | run: mv ${{ steps.sign_app.outputs.signedReleaseFile }} ./umalib-android-${{ github.ref_name }}.apk
39 | - name: Release
40 | uses: softprops/action-gh-release@v1
41 | with:
42 | tag_name: ${{ github.ref_name }}
43 | name: Uma Library Android ${{ github.ref_name }}
44 | body: built by Github Actions
45 | draft: false
46 | prerelease: false
47 | files: ./umalib-android-${{ github.ref_name }}.apk
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea
5 | .DS_Store
6 | /build
7 | /captures
8 | .externalNativeBuild
9 | .cxx
10 | local.properties
11 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Uma Library Android
2 |
3 | A library of doujin articles of _Umamusume: Pretty Derby_
4 |
5 | ## How to Contribute
6 |
7 | Forks and sends Pull Request to [UmaLibAndroid](https://github.com/umalib/UmaLibAndroid)
8 |
9 | ## Support
10 |
11 | Publishing post (Chinese): https://bbs.nga.cn/read.php?tid=33041357
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 | /release
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'org.greenrobot.greendao'
4 | id 'org.jetbrains.kotlin.android'
5 | }
6 |
7 | android {
8 | compileSdk 31
9 |
10 | defaultConfig {
11 | applicationId "cn.umafan.lib.android"
12 | minSdk 26
13 | targetSdk 31
14 | versionCode 2
15 | versionName "3.0.2"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 |
19 | buildFeatures {
20 | dataBinding = true
21 | }
22 | }
23 |
24 | buildTypes {
25 | release {
26 | minifyEnabled false
27 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
28 | }
29 | }
30 | compileOptions {
31 | sourceCompatibility JavaVersion.VERSION_1_8
32 | targetCompatibility JavaVersion.VERSION_1_8
33 | }
34 | kotlinOptions {
35 | jvmTarget = '1.8'
36 | }
37 | buildFeatures {
38 | viewBinding true
39 | }
40 | }
41 |
42 |
43 | dependencies {
44 | implementation project(path: ':preference')
45 |
46 | implementation 'androidx.appcompat:appcompat:1.4.1'
47 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
48 | implementation 'androidx.core:core-ktx:1.7.0'
49 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
50 | implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
51 | implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.1'
52 | implementation 'androidx.navigation:navigation-fragment-ktx:2.3.5'
53 | implementation 'androidx.navigation:navigation-ui-ktx:2.3.5'
54 | implementation 'com.github.angcyo:DslAdapter:4.1.0'
55 | implementation 'com.github.Ferfalk:SimpleSearchView:0.2.0'
56 | implementation 'com.google.android.material:material:1.6.0-alpha02'
57 | implementation 'org.greenrobot:greendao:3.3.0'
58 |
59 | implementation 'com.squareup.retrofit2:retrofit:2.9.0'
60 | implementation 'com.squareup.okhttp3:okhttp:5.0.0-alpha.3'
61 | implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
62 |
63 | implementation 'com.heinrichreimersoftware:material-intro:2.0.0'
64 | implementation 'com.github.tiagohm.MarkdownView:library:0.19.0'
65 | implementation 'com.github.getActivity:XXPermissions:16.0'
66 |
67 | implementation 'com.github.zzz40500:android-shapeLoadingView:1.0.3.2'
68 | implementation 'com.github.ldh-star:AndroidKit:0.1.5'
69 | implementation 'com.github.open-android:BridgeWebView:v1.0'
70 | implementation 'com.belerweb:pinyin4j:2.5.0'
71 |
72 | // 文件下载
73 | implementation "com.tonyodev.fetch2:fetch2:3.0.12"
74 | // 消息通知
75 | implementation "io.karn:notify:1.4.0"
76 | // 解压文件
77 |
78 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
79 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
80 |
81 | testImplementation 'junit:junit:4.13.2'
82 | }
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/app/src/androidTest/java/cn/umafan/lib/android/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android
2 |
3 | import androidx.test.ext.junit.runners.AndroidJUnit4
4 | import androidx.test.platform.app.InstrumentationRegistry
5 | import org.junit.Assert.assertEquals
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | /**
10 | * Instrumented test, which will execute on an Android device.
11 | *
12 | * See [testing documentation](http://d.android.com/tools/testing).
13 | */
14 | @RunWith(AndroidJUnit4::class)
15 | class ExampleInstrumentedTest {
16 | @Test
17 | fun useAppContext() {
18 | // Context of the app under test.
19 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
20 | assertEquals("cn.umafan.lib.android", appContext.packageName)
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
20 |
24 |
28 |
32 |
37 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
53 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/assets/db/main.db:
--------------------------------------------------------------------------------
1 | version https://git-lfs.github.com/spec/v1
2 | oid sha256:e0cef2348d6c73247b30a6079a9e8d19bc0d20b3aeff1728d0644061f1cdf0f4
3 | size 32768
4 |
--------------------------------------------------------------------------------
/app/src/main/assets/reader/css/umalib.reader.css:
--------------------------------------------------------------------------------
1 | div.theme-nga,
2 | div.theme-nga div.ql-editor,
3 | div.theme-nga div.el-descriptions .el-descriptions-item__cell {
4 | color: #10273f;
5 | background-color: #f5e8cb;
6 | }
7 |
8 | div.theme-cyan,
9 | div.theme-cyan div.ql-editor,
10 | div.theme-cyan div.el-descriptions .el-descriptions-item__cell {
11 | color: #10273f;
12 | background-color: #d3dedb;
13 | }
14 |
15 | div.theme-teio,
16 | div.theme-teio div.ql-editor,
17 | div.theme-teio div.el-descriptions .el-descriptions-item__cell {
18 | color: navy;
19 | background-color: #d0daff;
20 | }
21 |
22 | div.theme-purple,
23 | div.theme-purple div.ql-editor,
24 | div.theme-purple div.el-descriptions .el-descriptions-item__cell {
25 | color: #10273f;
26 | background-color: #ded1d4;
27 | }
28 |
29 | div.theme-black,
30 | div.theme-black div.ql-editor,
31 | div.theme-black div.el-descriptions .el-descriptions-item__cell {
32 | color: #ddedf5;
33 | background-color: #34312e;
34 | }
35 |
36 | div.theme-green,
37 | div.theme-green div.ql-editor,
38 | div.theme-green div.el-descriptions .el-descriptions-item__cell {
39 | color: black;
40 | background-color: #dde9dd;
41 | }
42 |
43 | div.theme-exhentai,
44 | div.theme-exhentai div.ql-editor,
45 | div.theme-exhentai div.el-descriptions .el-descriptions-item__cell {
46 | color: #f1f1f1;
47 | background-color: #3e424a;
48 | }
49 |
50 | div.theme-porn,
51 | div.theme-porn div.ql-editor,
52 | div.theme-porn div.el-descriptions .el-descriptions-item__cell {
53 | color: #e4e4e4;
54 | background-color: black;
55 | }
56 |
57 | div.el-descriptions .el-descriptions-item__label {
58 | min-width: 50px;
59 | }
60 |
61 | div.ql-editor img {
62 | max-width: 100%;
63 | }
64 |
65 | div.small-font div.ql-editor span.annotation,
66 | div.small-font div.ql-editor blockquote {
67 | font-size: 0.75em;
68 | }
69 |
70 | div.normal-font div.ql-editor h1 {
71 | font-size: 2.5em;
72 | }
73 |
74 | div.normal-font div.ql-editor h2 {
75 | font-size: 2.25em;
76 | }
77 |
78 | div.normal-font div.ql-editor p,
79 | div.normal-font div.ql-editor li {
80 | font-size: 1.5em;
81 | }
82 |
83 | div.normal-font div.ql-editor span.ql-size-large {
84 | font-size: 2em;
85 | }
86 |
87 | div.normal-font div.ql-editor span.ql-size-small,
88 | div.normal-font div.ql-editor span.annotation,
89 | div.normal-font div.ql-editor blockquote {
90 | font-size: 1em;
91 | }
92 |
93 | div.large-font div.ql-editor h1 {
94 | font-size: 3.75em;
95 | }
96 |
97 | div.large-font div.ql-editor h2 {
98 | font-size: 3.375em;
99 | }
100 |
101 | div.large-font div.ql-editor p,
102 | div.large-font div.ql-editor li {
103 | font-size: 2.25em;
104 | }
105 |
106 | div.large-font div.ql-editor span.ql-size-large {
107 | font-size: 3em;
108 | }
109 |
110 | div.large-font div.ql-editor span.ql-size-small,
111 | div.large-font div.ql-editor span.annotation,
112 | div.large-font div.ql-editor blockquote {
113 | font-size: 1.5em;
114 | }
115 |
116 | div.wider-space div.ql-editor p,
117 | div.wider-space div.ql-editor h1,
118 | div.wider-space div.ql-editor h2 {
119 | margin-bottom: 5px;
120 | }
121 |
122 | div.wider-space div.ql-editor p,
123 | div.wider-space div.ql-editor h1,
124 | div.wider-space div.ql-editor h2 {
125 | margin-bottom: 10px;
126 | }
127 |
128 | div.wider-space div.ql-editor p,
129 | div.wider-space div.ql-editor h1,
130 | div.wider-space div.ql-editor h2 {
131 | margin-bottom: 15px;
132 | }
133 |
134 | span + span.annotation {
135 | display: none;
136 | }
137 |
138 | span.key + span.annotation {
139 | display: block;
140 | border-left: 4px solid #ccc;
141 | margin-bottom: 5px;
142 | margin-top: 5px;
143 | padding-left: 16px;
144 | }
--------------------------------------------------------------------------------
/app/src/main/assets/reader/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | {{ article.name }}
15 | {{ article.author }}
16 |
17 | {{ article.translator }}
18 |
19 | {{ article.time }}
20 |
21 |
22 | {{ tag.name }}
23 |
24 |
25 |
26 |
27 |
28 | {{ src }}
29 |
30 |
31 |
32 | {{ article.note }}
33 |
34 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
99 |
--------------------------------------------------------------------------------
/app/src/main/assets/reader/js/index.js:
--------------------------------------------------------------------------------
1 | /**
2 | * 统一管理js调用安卓方法
3 | * @param method 方法名
4 | * @param params 参数 数组格式
5 | */
6 | var callAndroidMethod = function(method, params){
7 | window.jsInterface.invokeMethod(method, [JSON.stringify(params)]);//json对象转成字符串,再转成字符串数组
8 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/model/DataBaseHandler.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.model
2 |
3 | import android.os.Handler
4 | import android.os.Looper
5 | import android.os.Message
6 |
7 | /**
8 | * 自定义handler模板,每个数据库的操作都通过此handler构建
9 | */
10 | class DataBaseHandler(
11 | private val activity: MyBaseActivity,
12 | private val unit: (Message) -> Unit
13 | ) : Handler(Looper.getMainLooper()) {
14 | override fun handleMessage(msg: Message) {
15 | super.handleMessage(msg)
16 | when (msg.what) {
17 | // 若数据库在加载中,则展示进度条
18 | MyApplication.DATABASE_LOADING -> {
19 | activity.dataBaseLoadingDialog(msg.obj as Double)
20 | }
21 | MyApplication.DATABASE_LOADED -> {
22 | activity.dataBaseLoadingDialog(100.0)
23 | try {
24 | unit(msg)
25 | } catch (e: Exception) {
26 | e.printStackTrace()
27 | }
28 | activity.shapeLoadingDialog?.dialog?.hide()
29 | }
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/model/MyApplication.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.model
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Application
5 | import android.content.Context
6 |
7 | class MyApplication : Application() {
8 | companion object {
9 | @SuppressLint("StaticFieldLeak")
10 | lateinit var context: Context
11 | const val DATABASE_LOADING = 0
12 | const val DATABASE_LOADED = 1
13 |
14 | fun getVersion(): VersionBean {
15 | val v = context.packageManager.getPackageInfo(context.packageName, 0)
16 | return VersionBean(
17 | v.versionCode,
18 | v.versionName
19 | )
20 |
21 | }
22 | }
23 |
24 | override fun onCreate() {
25 | super.onCreate()
26 | context = applicationContext
27 | }
28 |
29 | }
30 |
31 | data class VersionBean(
32 | val code: Int,
33 | val name: String
34 | )
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/model/MyBaseViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.model
2 |
3 | import android.util.Log
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.viewModelScope
7 | import cn.umafan.lib.android.util.network.UpdateUtil
8 | import cn.umafan.lib.android.util.network.model.UpdateBean
9 | import com.liangguo.androidkit.app.ToastUtil
10 | import kotlinx.coroutines.launch
11 | import java.io.BufferedReader
12 | import java.io.BufferedWriter
13 | import java.io.FileReader
14 | import java.io.FileWriter
15 |
16 |
17 | class MyBaseViewModel : ViewModel() {
18 | val updateInfo = MutableLiveData()
19 |
20 | companion object {
21 | private val verFile = MyApplication.context.getDatabasePath("version")
22 | private var dbVersion = 0
23 |
24 | fun getDbVersion(): Int {
25 | verFile.parentFile?.mkdirs()
26 | var flag = !verFile.exists()
27 | if (dbVersion == 0) {
28 | try {
29 | val br = BufferedReader(FileReader(verFile))
30 | dbVersion = Integer.parseInt(br.readLine())
31 | br.close()
32 | } catch (e: Exception) {
33 | Log.e("MyBaseViewModel", e.message!!)
34 | flag = true
35 | }
36 | }
37 | if (flag) {
38 | setDbVersion(dbVersion)
39 | }
40 | return dbVersion
41 | }
42 |
43 | fun setDbVersion(version: Int) {
44 | dbVersion = version
45 | val bw = BufferedWriter(FileWriter(verFile))
46 | bw.write(version.toString())
47 | bw.close()
48 | Log.d("DBVersion", "set db version $dbVersion")
49 | }
50 | }
51 |
52 | fun getUpdate(activity: MyBaseActivity, initiative: Boolean) {
53 | viewModelScope.launch {
54 | UpdateUtil.getUpdate().apply {
55 | if (initiative) activity.shapeLoadingDialog?.dialog?.hide()
56 | if (null != this) {
57 | Log.d("DBVersion", getDbVersion().toString())
58 | this.show =
59 | if (this.currentVersionName > MyApplication.getVersion().name) 2
60 | else if (this.currentDb > getDbVersion()) 1
61 | else 0
62 | this.initiative = initiative
63 | updateInfo.value = this
64 | } else {
65 | ToastUtil.error("获取更新失败,请检查网络!")
66 | }
67 | }
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/model/PageSelectorViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.model
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import com.google.android.material.card.MaterialCardView
6 |
7 |
8 | open class PageSelectorViewModel : ViewModel() {
9 | var checkedList = mutableListOf()
10 |
11 | val checkedButton = MutableLiveData()
12 |
13 | val selectedPage = MutableLiveData(1)
14 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/model/SearchBean.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.model
2 |
3 | import cn.umafan.lib.android.model.db.Tag
4 | import java.io.Serializable
5 |
6 | data class SearchBean(
7 | var keyword: String? = "",
8 | var tags: MutableSet = mutableSetOf(),
9 | var creator: String? = "",
10 | var exceptedTags: MutableSet = mutableSetOf(),
11 | var isRandom: Boolean = false
12 | ) : Serializable
13 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/model/db/ArtCreator.java:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.model.db;
2 |
3 | import org.greenrobot.greendao.annotation.Entity;
4 | import org.greenrobot.greendao.annotation.Id;
5 | import org.greenrobot.greendao.annotation.Property;
6 | import org.greenrobot.greendao.annotation.Generated;
7 |
8 | @Entity(createInDb = false, nameInDb = "article")
9 | public class ArtCreator {
10 | @Id
11 | @Property(nameInDb = "id")
12 | private Long id;
13 |
14 | @Property(nameInDb = "author")
15 | private String author;
16 | @Property(nameInDb = "translator")
17 | private String translator;
18 |
19 | @Generated(hash = 1069894656)
20 | public ArtCreator(Long id, String author, String translator) {
21 | this.id = id;
22 | this.author = author;
23 | this.translator = translator;
24 | }
25 |
26 | @Generated(hash = 2043771935)
27 | public ArtCreator() {
28 | }
29 |
30 | public Long getId() {
31 | return this.id;
32 | }
33 |
34 | public void setId(Long id) {
35 | this.id = id;
36 | }
37 |
38 | public String getAuthor() {
39 | return this.author;
40 | }
41 |
42 | public void setAuthor(String author) {
43 | this.author = author;
44 | }
45 |
46 | public String getTranslator() {
47 | return this.translator;
48 | }
49 |
50 | public void setTranslator(String translator) {
51 | this.translator = translator;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/model/db/Creator.java:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.model.db;
2 |
3 | import org.greenrobot.greendao.annotation.Entity;
4 | import org.greenrobot.greendao.annotation.Generated;
5 | import org.greenrobot.greendao.annotation.Id;
6 | import org.greenrobot.greendao.annotation.Property;
7 |
8 | @Entity(createInDb = false)
9 | public class Creator {
10 | @Id
11 | @Property(nameInDb = "id")
12 | private Long id;
13 |
14 | @Property(nameInDb = "names")
15 | private String names;
16 |
17 | @Generated(hash = 1493267542)
18 | public Creator(Long id, String names) {
19 | this.id = id;
20 | this.names = names;
21 | }
22 |
23 | @Generated(hash = 908439796)
24 | public Creator() {
25 | }
26 |
27 | public Long getId() {
28 | return this.id;
29 | }
30 |
31 | public void setId(Long id) {
32 | this.id = id;
33 | }
34 |
35 | public String getNames() {
36 | return this.names;
37 | }
38 |
39 | public void setNames(String names) {
40 | this.names = names;
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/model/db/Dict.java:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.model.db;
2 |
3 | import org.greenrobot.greendao.annotation.Entity;
4 | import org.greenrobot.greendao.annotation.Id;
5 | import org.greenrobot.greendao.annotation.Property;
6 |
7 | import java.io.Serializable;
8 | import org.greenrobot.greendao.annotation.Generated;
9 |
10 | @Entity(createInDb = false)
11 | public class Dict implements Serializable {
12 | private static final long serialVersionUID = 1L;
13 |
14 | @Id
15 | @Property(nameInDb = "id")
16 | private Long id;
17 |
18 | @Property(nameInDb = "class")
19 | private String classType;
20 |
21 | @Property(nameInDb = "desc")
22 | private String desc;
23 |
24 | @Property(nameInDb = "key")
25 | private String key;
26 |
27 | @Property(nameInDb = "refId")
28 | private Integer refId;
29 |
30 | @Property(nameInDb = "related")
31 | private String related;
32 |
33 | @Property(nameInDb = "relatedId")
34 | private Integer relatedId;
35 |
36 | @Generated(hash = 1019137228)
37 | public Dict(Long id, String classType, String desc, String key, Integer refId,
38 | String related, Integer relatedId) {
39 | this.id = id;
40 | this.classType = classType;
41 | this.desc = desc;
42 | this.key = key;
43 | this.refId = refId;
44 | this.related = related;
45 | this.relatedId = relatedId;
46 | }
47 |
48 | @Generated(hash = 1138334630)
49 | public Dict() {
50 | }
51 |
52 | public Long getId() {
53 | return this.id;
54 | }
55 |
56 | public void setId(Long id) {
57 | this.id = id;
58 | }
59 |
60 | public String getClassType() {
61 | return this.classType;
62 | }
63 |
64 | public void setClassType(String classType) {
65 | this.classType = classType;
66 | }
67 |
68 | public String getDesc() {
69 | return this.desc;
70 | }
71 |
72 | public void setDesc(String desc) {
73 | this.desc = desc;
74 | }
75 |
76 | public String getKey() {
77 | return this.key;
78 | }
79 |
80 | public void setKey(String key) {
81 | this.key = key;
82 | }
83 |
84 | public Integer getRefId() {
85 | return this.refId;
86 | }
87 |
88 | public void setRefId(Integer refId) {
89 | this.refId = refId;
90 | }
91 |
92 | public String getRelated() {
93 | return this.related;
94 | }
95 |
96 | public void setRelated(String related) {
97 | this.related = related;
98 | }
99 |
100 | public Integer getRelatedId() {
101 | return this.relatedId;
102 | }
103 |
104 | public void setRelatedId(Integer relatedId) {
105 | this.relatedId = relatedId;
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/model/db/Rec.java:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.model.db;
2 |
3 | import org.greenrobot.greendao.annotation.Entity;
4 | import org.greenrobot.greendao.annotation.Id;
5 | import org.greenrobot.greendao.annotation.Property;
6 |
7 | import java.io.Serializable;
8 | import org.greenrobot.greendao.annotation.Generated;
9 |
10 | @Entity(createInDb = false)
11 | public class Rec implements Serializable {
12 | private static final long serialVersionUID = 1L;
13 |
14 | @Id
15 | @Property(nameInDb = "id")
16 | private Long id;
17 |
18 | @Property(nameInDb = "refId")
19 | private Long refId;
20 |
21 | @Property(nameInDb = "title")
22 | private String title;
23 |
24 | @Property(nameInDb = "others")
25 | private String others;
26 |
27 | @Property(nameInDb = "type")
28 | private Integer type;
29 |
30 | @Property(nameInDb = "r")
31 | private Integer r;
32 |
33 | @Property(nameInDb = "name")
34 | private String name;
35 |
36 | @Property(nameInDb = "reason")
37 | private String reason;
38 |
39 | @Generated(hash = 1774498343)
40 | public Rec(Long id, Long refId, String title, String others, Integer type,
41 | Integer r, String name, String reason) {
42 | this.id = id;
43 | this.refId = refId;
44 | this.title = title;
45 | this.others = others;
46 | this.type = type;
47 | this.r = r;
48 | this.name = name;
49 | this.reason = reason;
50 | }
51 |
52 | @Generated(hash = 424129248)
53 | public Rec() {
54 | }
55 |
56 | public Long getId() {
57 | return this.id;
58 | }
59 |
60 | public void setId(Long id) {
61 | this.id = id;
62 | }
63 |
64 | public Long getRefId() {
65 | return this.refId;
66 | }
67 |
68 | public void setRefId(Long refId) {
69 | this.refId = refId;
70 | }
71 |
72 | public String getTitle() {
73 | return this.title;
74 | }
75 |
76 | public void setTitle(String title) {
77 | this.title = title;
78 | }
79 |
80 | public String getOthers() {
81 | return this.others;
82 | }
83 |
84 | public void setOthers(String others) {
85 | this.others = others;
86 | }
87 |
88 | public Integer getType() {
89 | return this.type;
90 | }
91 |
92 | public void setType(Integer type) {
93 | this.type = type;
94 | }
95 |
96 | public Integer getR() {
97 | return this.r;
98 | }
99 |
100 | public void setR(Integer r) {
101 | this.r = r;
102 | }
103 |
104 | public String getName() {
105 | return this.name;
106 | }
107 |
108 | public void setName(String name) {
109 | this.name = name;
110 | }
111 |
112 | public String getReason() {
113 | return this.reason;
114 | }
115 |
116 | public void setReason(String reason) {
117 | this.reason = reason;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/model/db/Tag.java:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.model.db;
2 |
3 | import org.greenrobot.greendao.DaoException;
4 | import org.greenrobot.greendao.annotation.Entity;
5 | import org.greenrobot.greendao.annotation.Generated;
6 | import org.greenrobot.greendao.annotation.Id;
7 | import org.greenrobot.greendao.annotation.Property;
8 | import org.greenrobot.greendao.annotation.ToMany;
9 |
10 | import java.io.Serializable;
11 | import java.util.List;
12 |
13 | @Entity(createInDb = false)
14 | public class Tag implements Serializable {
15 | private static final long serialVersionUID = 1L;
16 | @Id
17 | @Property(nameInDb = "id")
18 | private Long id;
19 |
20 | @Property(nameInDb = "name")
21 | private String name;
22 | @Property(nameInDb = "type")
23 | private Integer type;
24 | @Property(nameInDb = "cover")
25 | private String cover;
26 | @Property(nameInDb = "description")
27 | private String description;
28 |
29 | @ToMany(referencedJoinProperty = "tagId")
30 | private List taggedList;
31 | /**
32 | * Used to resolve relations
33 | */
34 | @Generated(hash = 2040040024)
35 | private transient DaoSession daoSession;
36 | /**
37 | * Used for active entity operations.
38 | */
39 | @Generated(hash = 2076396065)
40 | private transient TagDao myDao;
41 |
42 | @Generated(hash = 1132560429)
43 | public Tag(Long id, String name, Integer type, String cover,
44 | String description) {
45 | this.id = id;
46 | this.name = name;
47 | this.type = type;
48 | this.cover = cover;
49 | this.description = description;
50 | }
51 |
52 | @Generated(hash = 1605720318)
53 | public Tag() {
54 | }
55 |
56 | public Long getId() {
57 | return this.id;
58 | }
59 |
60 | public void setId(Long id) {
61 | this.id = id;
62 | }
63 |
64 | public String getName() {
65 | return this.name;
66 | }
67 |
68 | public void setName(String name) {
69 | this.name = name;
70 | }
71 |
72 | public Integer getType() {
73 | return this.type;
74 | }
75 |
76 | public void setType(Integer type) {
77 | this.type = type;
78 | }
79 |
80 | public String getCover() {
81 | return this.cover;
82 | }
83 |
84 | public void setCover(String cover) {
85 | this.cover = cover;
86 | }
87 |
88 | public String getDescription() {
89 | return this.description;
90 | }
91 |
92 | public void setDescription(String description) {
93 | this.description = description;
94 | }
95 |
96 | /**
97 | * To-many relationship, resolved on first access (and after reset).
98 | * Changes to to-many relations are not persisted, make changes to the target entity.
99 | */
100 | @Generated(hash = 273028975)
101 | public List getTaggedList() {
102 | if (taggedList == null) {
103 | final DaoSession daoSession = this.daoSession;
104 | if (daoSession == null) {
105 | throw new DaoException("Entity is detached from DAO context");
106 | }
107 | TaggedDao targetDao = daoSession.getTaggedDao();
108 | List taggedListNew = targetDao._queryTag_TaggedList(id);
109 | synchronized (this) {
110 | if (taggedList == null) {
111 | taggedList = taggedListNew;
112 | }
113 | }
114 | }
115 | return taggedList;
116 | }
117 |
118 | /**
119 | * Resets a to-many relationship, making the next get call to query for a fresh result.
120 | */
121 | @Generated(hash = 300101849)
122 | public synchronized void resetTaggedList() {
123 | taggedList = null;
124 | }
125 |
126 | /**
127 | * Convenient call for {@link org.greenrobot.greendao.AbstractDao#delete(Object)}.
128 | * Entity must attached to an entity context.
129 | */
130 | @Generated(hash = 128553479)
131 | public void delete() {
132 | if (myDao == null) {
133 | throw new DaoException("Entity is detached from DAO context");
134 | }
135 | myDao.delete(this);
136 | }
137 |
138 | /**
139 | * Convenient call for {@link org.greenrobot.greendao.AbstractDao#refresh(Object)}.
140 | * Entity must attached to an entity context.
141 | */
142 | @Generated(hash = 1942392019)
143 | public void refresh() {
144 | if (myDao == null) {
145 | throw new DaoException("Entity is detached from DAO context");
146 | }
147 | myDao.refresh(this);
148 | }
149 |
150 | /**
151 | * Convenient call for {@link org.greenrobot.greendao.AbstractDao#update(Object)}.
152 | * Entity must attached to an entity context.
153 | */
154 | @Generated(hash = 713229351)
155 | public void update() {
156 | if (myDao == null) {
157 | throw new DaoException("Entity is detached from DAO context");
158 | }
159 | myDao.update(this);
160 | }
161 |
162 | /** called by internal mechanisms, do not call yourself. */
163 | @Generated(hash = 441429822)
164 | public void __setDaoSession(DaoSession daoSession) {
165 | this.daoSession = daoSession;
166 | myDao = daoSession != null ? daoSession.getTagDao() : null;
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/MainIntroActivity.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui
2 |
3 | import android.os.Bundle
4 | import cn.umafan.lib.android.R
5 | import com.heinrichreimersoftware.materialintro.app.IntroActivity
6 | import com.heinrichreimersoftware.materialintro.slide.SimpleSlide
7 |
8 |
9 | class MainIntroActivity : IntroActivity() {
10 | override fun onCreate(savedInstanceState: Bundle?) {
11 | isFullscreen = true
12 | super.onCreate(savedInstanceState)
13 |
14 | addSlide(
15 | SimpleSlide.Builder()
16 | .title(R.string.app_name)
17 | .description(R.string.app_description)
18 | .image(R.drawable.ic_launcher)
19 | .background(R.color.white)
20 | .backgroundDark(R.color.black)
21 | .scrollable(false)
22 | .build()
23 | )
24 |
25 | addSlide(
26 | SimpleSlide.Builder()
27 | .title(R.string.app_database_intro)
28 | .description(R.string.app_database_description)
29 | .image(R.drawable.app_database_intro)
30 | .background(R.color.blue_500)
31 | .backgroundDark(R.color.blue_500)
32 | .scrollable(false)
33 | .build()
34 | )
35 |
36 | addSlide(
37 | SimpleSlide.Builder()
38 | .title(R.string.app_view_intro)
39 | .description(R.string.app_view_description)
40 | .image(R.drawable.app_view_intro)
41 | .background(R.color.purple_500)
42 | .backgroundDark(R.color.purple_500)
43 | .scrollable(false)
44 | .build()
45 | )
46 |
47 | addSlide(
48 | SimpleSlide.Builder()
49 | .title(R.string.app_tag_intro)
50 | .description(R.string.app_tag_description)
51 | .image(R.drawable.app_tag_intro)
52 | .background(R.color.teal_200)
53 | .backgroundDark(R.color.teal_200)
54 | .scrollable(false)
55 | .build()
56 | )
57 |
58 | addSlide(
59 | SimpleSlide.Builder()
60 | .title(R.string.app_func_intro)
61 | .description(R.string.app_func_description)
62 | .image(R.drawable.app_func_intro)
63 | .background(R.color.lightPurple)
64 | .backgroundDark(R.color.lightPurple)
65 | .scrollable(false)
66 | .build()
67 | )
68 | }
69 |
70 | override fun onBackPressed() {}
71 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/UpdateLogActivity.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui
2 |
3 | import android.os.Bundle
4 | import android.view.MenuItem
5 | import br.tiagohm.markdownview.css.styles.Github
6 | import cn.umafan.lib.android.databinding.ActivityUpdateLogBinding
7 | import cn.umafan.lib.android.model.MyBaseActivity
8 |
9 | class UpdateLogActivity : MyBaseActivity() {
10 |
11 | private lateinit var binding: ActivityUpdateLogBinding
12 |
13 | override fun onCreate(savedInstanceState: Bundle?) {
14 | super.onCreate(savedInstanceState)
15 |
16 | binding = ActivityUpdateLogBinding.inflate(layoutInflater)
17 | setContentView(binding.root)
18 |
19 | setSupportActionBar(binding.toolbar)
20 | supportActionBar!!.setDisplayHomeAsUpEnabled(true)
21 |
22 | binding.markdownView.addStyleSheet(Github())
23 | .loadMarkdownFromUrl("https://umalib.github.io/UmaLibAndroid/update-log.md")
24 | }
25 |
26 | override fun onOptionsItemSelected(item: MenuItem): Boolean {
27 | if (item.itemId == android.R.id.home) {
28 | onBackPressed()
29 | return true
30 | }
31 | return super.onOptionsItemSelected(item)
32 | }
33 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/favorites/FavoritesViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.favorites
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.viewModelScope
5 | import cn.umafan.lib.android.model.PageSelectorViewModel
6 | import cn.umafan.lib.android.model.db.ArtInfo
7 | import cn.umafan.lib.android.ui.home.model.ArticleInfoItem
8 | import cn.umafan.lib.android.ui.home.model.PageItem
9 | import com.angcyo.dsladapter.DslAdapter
10 | import kotlinx.coroutines.flow.MutableStateFlow
11 | import kotlinx.coroutines.launch
12 |
13 | class FavoritesViewModel : PageSelectorViewModel() {
14 |
15 | private val articleData = MutableStateFlow(listOf())
16 |
17 | val currentPage = MutableLiveData(1)
18 |
19 | val articleDataAdapter = DslAdapter()
20 |
21 | val pageSelectorAdapter = DslAdapter()
22 |
23 | val pageData = MutableStateFlow(listOf())
24 |
25 | val pageLen = MutableLiveData(1)
26 |
27 | init {
28 | viewModelScope.launch {
29 | articleData.collect {
30 | articleDataAdapter.changeDataItems { adapterItems ->
31 | adapterItems.clear()
32 | it.forEach {
33 | adapterItems.add(ArticleInfoItem(it))
34 | }
35 | }
36 | }
37 | }
38 | viewModelScope.launch {
39 | pageData.collect {
40 | pageSelectorAdapter.changeDataItems { adapterItems ->
41 | adapterItems.clear()
42 | it.forEach {
43 | adapterItems.add(PageItem(it, this@FavoritesViewModel))
44 | }
45 | }
46 | pageSelectorAdapter.notifyDataChanged()
47 | }
48 | }
49 | }
50 |
51 | fun loadArticles(data: MutableList) {
52 | viewModelScope.launch {
53 | articleData.emit(data)
54 | }
55 | }
56 |
57 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/history/HistoryFragment.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.history
2 |
3 | import android.annotation.SuppressLint
4 | import android.graphics.drawable.Drawable
5 | import android.os.Bundle
6 | import android.view.LayoutInflater
7 | import android.view.View
8 | import android.view.ViewGroup
9 | import androidx.fragment.app.Fragment
10 | import androidx.lifecycle.ViewModelProvider
11 | import cn.umafan.lib.android.R
12 | import cn.umafan.lib.android.databinding.FragmentHistroyBinding
13 | import cn.umafan.lib.android.model.DataBaseHandler
14 | import cn.umafan.lib.android.model.MyBaseActivity
15 | import cn.umafan.lib.android.model.db.ArtInfo
16 | import cn.umafan.lib.android.model.db.ArtInfoDao
17 | import cn.umafan.lib.android.model.db.DaoSession
18 | import cn.umafan.lib.android.ui.main.DatabaseCopyThread
19 | import cn.umafan.lib.android.ui.main.MainActivity
20 | import cn.umafan.lib.android.util.HistoryUtil
21 | import cn.umafan.lib.android.util.SettingUtil
22 | import com.liangguo.androidkit.app.ToastUtil
23 |
24 | @SuppressLint("InflateParams")
25 | class HistoryFragment : Fragment() {
26 | private var _binding: FragmentHistroyBinding? = null
27 |
28 | private val binding get() = _binding!!
29 |
30 | private lateinit var _historyViewModel: HistoryViewModel
31 |
32 | private val historyViewModel get() = _historyViewModel
33 |
34 | private var daoSession: DaoSession? = null
35 |
36 | @SuppressLint("SetTextI18n")
37 | override fun onCreateView(
38 | inflater: LayoutInflater,
39 | container: ViewGroup?,
40 | savedInstanceState: Bundle?
41 | ): View {
42 | _historyViewModel =
43 | ViewModelProvider(this)[HistoryViewModel::class.java]
44 |
45 | _binding = FragmentHistroyBinding.inflate(inflater, container, false)
46 | val root: View = binding.root
47 |
48 | initViews()
49 |
50 | loadArticles()
51 |
52 | return root
53 | }
54 |
55 | private fun initViews() {
56 | with(binding) {
57 | recyclerView.adapter = historyViewModel.articleDataAdapter
58 | layout.apply {
59 | val uri = SettingUtil.getImageBackground(SettingUtil.INDEX_BG)
60 | if (null != uri) background = Drawable.createFromPath(uri.path)
61 | }
62 | }
63 | }
64 |
65 | private fun loadArticles() {
66 | val idList = HistoryUtil.getHistory()
67 | if (idList.isEmpty()) {
68 | ToastUtil.info(getString(R.string.no_data))
69 | }
70 | val handler = DataBaseHandler(activity as MyBaseActivity) {
71 | daoSession = it.obj as DaoSession
72 | if (null != daoSession) {
73 | val artInfoDao: ArtInfoDao = daoSession!!.artInfoDao
74 | val data = mutableListOf()
75 | idList.forEach { id ->
76 | val art =
77 | artInfoDao.queryBuilder().where(ArtInfoDao.Properties.Id.eq(id)).unique()
78 | if (null != art) data.add(art)
79 | }
80 | historyViewModel.loadArticles(data)
81 | }
82 | }
83 | (activity as MainActivity).shapeLoadingDialog?.show()
84 | DatabaseCopyThread.addHandler(handler)
85 | }
86 |
87 | override fun onResume() {
88 | super.onResume()
89 | loadArticles()
90 | }
91 |
92 | override fun onDestroyView() {
93 | super.onDestroyView()
94 | _binding = null
95 | }
96 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/history/HistoryViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.history
2 |
3 | import androidx.lifecycle.viewModelScope
4 | import cn.umafan.lib.android.model.PageSelectorViewModel
5 | import cn.umafan.lib.android.model.db.ArtInfo
6 | import cn.umafan.lib.android.ui.home.model.ArticleInfoItem
7 | import com.angcyo.dsladapter.DslAdapter
8 | import kotlinx.coroutines.flow.MutableStateFlow
9 | import kotlinx.coroutines.launch
10 |
11 | class HistoryViewModel : PageSelectorViewModel() {
12 |
13 | private val articleData = MutableStateFlow(listOf())
14 |
15 | val articleDataAdapter = DslAdapter()
16 |
17 | init {
18 | viewModelScope.launch {
19 | articleData.collect {
20 | articleDataAdapter.changeDataItems { adapterItems ->
21 | adapterItems.clear()
22 | it.forEach {
23 | adapterItems.add(ArticleInfoItem(it))
24 | }
25 | }
26 | }
27 | }
28 | }
29 |
30 | fun loadArticles(data: MutableList) {
31 | viewModelScope.launch {
32 | articleData.emit(data)
33 | }
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/home/HomeViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.home
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.viewModelScope
6 | import cn.umafan.lib.android.model.PageSelectorViewModel
7 | import cn.umafan.lib.android.model.db.ArtInfo
8 | import cn.umafan.lib.android.ui.home.model.ArticleInfoItem
9 | import cn.umafan.lib.android.ui.home.model.PageItem
10 | import com.angcyo.dsladapter.DslAdapter
11 | import kotlinx.coroutines.flow.MutableStateFlow
12 | import kotlinx.coroutines.launch
13 |
14 | class HomeViewModel : PageSelectorViewModel() {
15 | private val _text = MutableLiveData().apply {
16 | value = "This is home Fragment"
17 | }
18 | val text: LiveData = _text
19 |
20 | val pageLen = MutableLiveData(1)
21 |
22 | private val articleData = MutableStateFlow(listOf())
23 |
24 | val currentPage = MutableLiveData(1)
25 |
26 | val articleDataAdapter = DslAdapter()
27 |
28 | val pageSelectorAdapter = DslAdapter()
29 |
30 | val pageData = MutableStateFlow(listOf())
31 |
32 | init {
33 | viewModelScope.launch {
34 | articleData.collect {
35 | articleDataAdapter.changeDataItems { adapterItems ->
36 | adapterItems.clear()
37 | it.forEach {
38 | adapterItems.add(ArticleInfoItem(it))
39 | }
40 | }
41 | }
42 | }
43 | viewModelScope.launch {
44 | pageData.collect {
45 | pageSelectorAdapter.changeDataItems { adapterItems ->
46 | adapterItems.clear()
47 | it.forEach {
48 | adapterItems.add(PageItem(it, this@HomeViewModel))
49 | }
50 | }
51 | pageSelectorAdapter.notifyDataChanged()
52 | }
53 | }
54 | }
55 |
56 | /**
57 | * 异步加载文章
58 | */
59 | fun loadArticles(list: List?) {
60 | viewModelScope.launch {
61 | var data = list
62 | if (null == data) {
63 | data = mutableListOf()
64 | }
65 | articleData.emit(data)
66 | }
67 | }
68 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/home/model/ArticleInfoItem.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.home.model
2 |
3 | import androidx.databinding.DataBindingUtil
4 | import cn.umafan.lib.android.R
5 | import cn.umafan.lib.android.databinding.ItemArticleCardBinding
6 | import cn.umafan.lib.android.model.db.ArtInfo
7 | import cn.umafan.lib.android.ui.reader.ReaderActivity
8 | import com.angcyo.dsladapter.DslAdapterItem
9 | import com.angcyo.dsladapter.DslViewHolder
10 | import com.google.android.material.snackbar.Snackbar
11 | import com.liangguo.androidkit.app.startNewActivity
12 | import java.text.SimpleDateFormat
13 | import java.util.*
14 |
15 | class ArticleInfoItem(
16 | private val articleInfo: ArtInfo
17 | ) : DslAdapterItem() {
18 | override var itemLayoutId = R.layout.item_article_card
19 | private val timeStampFormatter = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA)
20 |
21 | init {
22 | itemData = articleInfo
23 | thisAreContentsTheSame = { fromItem, newItem, _, _ ->
24 | fromItem?.itemData == newItem.itemData
25 | }
26 | thisAreItemsTheSame = { fromItem, newItem, _, _ ->
27 | fromItem?.itemData == newItem.itemData
28 | }
29 | }
30 |
31 | override fun onItemBind(
32 | itemHolder: DslViewHolder,
33 | itemPosition: Int,
34 | adapterItem: DslAdapterItem,
35 | payloads: List
36 | ) {
37 | super.onItemBind(itemHolder, itemPosition, adapterItem, payloads)
38 | itemHolder.view(R.id.item_article_card)?.let {
39 | DataBindingUtil.bind(it)?.apply {
40 | articleName.text = articleInfo.name
41 | articleNote.text = articleInfo.note
42 | if (articleInfo.translator.isNotEmpty()) {
43 | articleAuthor.text = articleInfo.author
44 | articleTranslator.text = String.format("译者:%s", articleInfo.translator)
45 | } else {
46 | articleAuthor.text = ""
47 | articleTranslator.text = articleInfo.author
48 | }
49 |
50 | articleUploadTime.text = articleInfo.uploadTime.let { date ->
51 | timeStampFormatter.format(date.toLong() * 1000)
52 | }
53 | articleTags.text =
54 | articleInfo.taggedList.map { tagged -> tagged.tag }
55 | .sortedWith { a, b ->
56 | if (a.type == b.type)
57 | a.name.compareTo(b.name)
58 | else
59 | b.type.compareTo(a.type)
60 | }.joinToString(" | ") { tag -> tag.name }
61 | itemArticleCardBox.setOnClickListener {
62 | ReaderActivity::class.startNewActivity {
63 | putExtra("id", articleInfo.id.toInt())
64 | }
65 | }
66 | itemArticleCardBox.setOnLongClickListener { view ->
67 | Snackbar.make(
68 | view,
69 | "[ID${articleInfo.id}]${articleInfo.name}",
70 | Snackbar.LENGTH_SHORT
71 | ).show()
72 | false
73 | }
74 | invalidateAll()
75 | }
76 | }
77 | }
78 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/home/model/PageItem.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.home.model
2 |
3 | import androidx.databinding.DataBindingUtil
4 | import cn.umafan.lib.android.R
5 | import cn.umafan.lib.android.databinding.ItemPageButtonBinding
6 | import cn.umafan.lib.android.model.PageSelectorViewModel
7 | import com.angcyo.dsladapter.DslAdapterItem
8 | import com.angcyo.dsladapter.DslViewHolder
9 |
10 | class PageItem(
11 | private val page: Int,
12 | private val mViewModel: PageSelectorViewModel
13 | ) : DslAdapterItem() {
14 | override var itemLayoutId = R.layout.item_page_button
15 |
16 | init {
17 | itemData = page
18 | thisAreContentsTheSame = { fromItem, newItem, _, _ ->
19 | fromItem?.itemData == newItem.itemData
20 | }
21 | thisAreItemsTheSame = { fromItem, newItem, _, _ ->
22 | fromItem?.itemData == newItem.itemData
23 | }
24 | }
25 |
26 | override fun onItemBind(
27 | itemHolder: DslViewHolder,
28 | itemPosition: Int,
29 | adapterItem: DslAdapterItem,
30 | payloads: List
31 | ) {
32 | super.onItemBind(itemHolder, itemPosition, adapterItem, payloads)
33 | itemHolder.view(R.id.item_page_button)?.let {
34 | DataBindingUtil.bind(it)?.apply {
35 | //先初始化使其不被选中,再通过数组储存的状态恢复选中
36 | itemPageButtonBox.isChecked = false
37 | itemPageButtonBox.isChecked = mViewModel.checkedList[page - 1]
38 | if (mViewModel.checkedList[page - 1]) {
39 | mViewModel.checkedButton.value = itemPageButtonBox
40 | }
41 | pageNumText.text = page.toString()
42 | itemPageButtonBox.setOnClickListener {
43 | itemPageButtonBox.isChecked = true
44 | with(mViewModel) {
45 | checkedButton.value?.isChecked = false
46 | checkedButton.value = itemPageButtonBox
47 | //清除数组再重新记录状态
48 | checkedList.replaceAll { false }
49 | checkedList[page - 1] = true
50 |
51 | selectedPage.value = page
52 | }
53 |
54 | }
55 | invalidateAll()
56 | }
57 | }
58 | }
59 |
60 | override fun onItemViewRecycled(itemHolder: DslViewHolder, itemPosition: Int) {
61 | itemHolder.view(R.id.item_page_button)?.let {
62 | DataBindingUtil.bind(it)?.apply {
63 | itemPageButtonBox.setOnCheckedChangeListener(null)
64 | }
65 | }
66 | super.onItemViewRecycled(itemHolder, itemPosition)
67 | }
68 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/main/DatabaseCopyThread.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.main
2 |
3 | import android.content.Context
4 | import android.os.Handler
5 | import android.util.Log
6 | import cn.umafan.lib.android.model.MyApplication
7 | import cn.umafan.lib.android.model.db.DaoMaster
8 | import cn.umafan.lib.android.model.db.DaoSession
9 | import cn.umafan.lib.android.util.ZipUtil
10 | import org.greenrobot.greendao.database.Database
11 | import java.io.File
12 | import java.io.FileOutputStream
13 | import java.io.InputStream
14 | import java.io.OutputStream
15 |
16 |
17 | class DatabaseCopyThread : Thread() {
18 | val context = MyApplication.context
19 | private lateinit var handler: Handler
20 |
21 | companion object {
22 | private var daoSession: DaoSession? = null
23 | private val queue = mutableListOf>()
24 | private val lock = Object()
25 |
26 | fun addHandler(_handler: Handler, fileName: String? = null) {
27 | queue.add(Pair(_handler, fileName))
28 | synchronized(lock) {
29 | lock.notify()
30 | }
31 | }
32 |
33 | fun clearDb() {
34 | daoSession = null
35 | }
36 |
37 | fun reload() {
38 | MyApplication.context.getDatabasePath("main.db").delete()
39 | }
40 | }
41 |
42 | override fun run() {
43 | while (true) {
44 | while (queue.size > 0) {
45 | handler = queue.first().first
46 | val dbName = queue.first().second
47 | queue.removeFirst()
48 | if (null == daoSession) {
49 | copyDatabase(dbName)
50 | if (context.getDatabasePath("main.db").exists()) {
51 | val helper = LibOpenHelper(context, "main.db")
52 | val db: Database = helper.readableDb
53 | daoSession = DaoMaster(db).newSession()
54 | }
55 | }
56 | val message = handler.obtainMessage()
57 | message.what = MyApplication.DATABASE_LOADED
58 | message.obj = daoSession
59 | handler.sendMessage(message)
60 | }
61 | synchronized(lock) {
62 | lock.wait()
63 | }
64 | }
65 | }
66 |
67 | private fun copyDatabase(name: String? = null) {
68 | val dbFile: File = context.getDatabasePath("main.db")
69 | try {
70 | dbFile.parentFile?.mkdirs()
71 | if (null !== name) {
72 | if (!dbFile.exists()) {
73 | dbFile.createNewFile()
74 | }
75 | val output: String = dbFile.parent!! + "/"
76 | val input: String = context.getDatabasePath(name).path
77 | val unzippedFileName = ZipUtil.unzip(input, output)
78 | val unzippedFile = context.getDatabasePath(unzippedFileName)
79 | if (unzippedFile.exists()) {
80 | unzippedFile.renameTo(context.getDatabasePath("main.db"))
81 | // 删除压缩包
82 | context.getDatabasePath(name).delete()
83 | } else {
84 | return
85 | }
86 | Log.i(this.javaClass.simpleName, "unzip database done!")
87 | } else if (!dbFile.exists()) {
88 | val inputStream: InputStream = context.assets.open("db/main.db")
89 | val outputStream: OutputStream = FileOutputStream(dbFile)
90 | val buffer = ByteArray(5)
91 | var length: Int = inputStream.read(buffer)
92 | val total = inputStream.available()
93 | var count = 0
94 |
95 | while (length > 0) {
96 | if (count % 12000 == 0) {
97 | val progress = count * length / total.toDouble() * 100
98 | val message = handler.obtainMessage()
99 | message.what = MyApplication.DATABASE_LOADING
100 | message.obj = progress
101 | handler.sendMessage(message)
102 | }
103 | outputStream.write(buffer, 0, length)
104 | length = inputStream.read(buffer)
105 | count++
106 | }
107 | outputStream.flush()
108 | outputStream.close()
109 | inputStream.close()
110 | Log.i(this.javaClass.simpleName, "copy database done!")
111 | }
112 | } catch (e: Exception) {
113 | e.printStackTrace()
114 | if (dbFile.exists()) {
115 | dbFile.delete()
116 | }
117 | }
118 | }
119 |
120 | class LibOpenHelper(val context: Context, val name: String) :
121 | DaoMaster.OpenHelper(context, name) {
122 |
123 | override fun onCreate(db: Database?) {
124 | super.onCreate(db)
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/main/MainViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.main
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import cn.umafan.lib.android.model.SearchBean
6 | import cn.umafan.lib.android.model.db.Tag
7 | import kotlinx.coroutines.flow.MutableStateFlow
8 |
9 | class MainViewModel : ViewModel() {
10 | var searchParams = MutableLiveData(SearchBean())
11 | var selectedTags = MutableStateFlow(mutableSetOf())
12 | var selectedExceptTags = MutableStateFlow(mutableSetOf())
13 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/main/model/CreatorSuggestionAdapter.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.main.model
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.widget.BaseAdapter
7 | import android.widget.Filter
8 | import android.widget.Filterable
9 | import cn.umafan.lib.android.R
10 | import cn.umafan.lib.android.model.MyApplication
11 | import cn.umafan.lib.android.util.PinyinUtil
12 | import com.google.android.material.textview.MaterialTextView
13 | import java.util.*
14 |
15 |
16 | /**
17 | * 用于搜索展示tag的适配器
18 | */
19 | class CreatorSuggestionAdapter(
20 | private val tags: List
21 | ) : BaseAdapter(), Filterable {
22 |
23 | private var mArrayFilter: ArrayFilter? = null
24 | private var filterTags: List = tags
25 |
26 | override fun getFilter(): Filter {
27 | if (mArrayFilter == null) {
28 | mArrayFilter = ArrayFilter(tags, this)
29 | }
30 | return mArrayFilter!!
31 | }
32 |
33 | override fun getItem(p0: Int): Any {
34 | return filterTags[p0]
35 | }
36 |
37 | override fun getItemId(p0: Int): Long {
38 | return p0.toLong()
39 | }
40 |
41 | override fun getCount(): Int {
42 | return filterTags.size
43 | }
44 |
45 | override fun getView(positon: Int, convertView: View?, parent: ViewGroup?): View {
46 | val viewHolder: ViewHolder?
47 | var mConvertView: View? = convertView
48 | if (null == mConvertView) {
49 | viewHolder = ViewHolder()
50 | mConvertView = LayoutInflater.from(MyApplication.context)
51 | .inflate(R.layout.item_tag_suggestion, null)
52 | viewHolder.tagName = mConvertView.findViewById(R.id.item_tag_suggestion_name)
53 | mConvertView.tag = viewHolder
54 | } else {
55 | viewHolder = mConvertView.tag as ViewHolder
56 | }
57 | val tag = filterTags[positon]
58 | viewHolder.tagName?.text = tag
59 |
60 | return mConvertView!!
61 | }
62 |
63 | class ViewHolder(
64 | var tagName: MaterialTextView? = null
65 | )
66 |
67 | private class ArrayFilter(
68 | private val tags: List,
69 | val adapter: CreatorSuggestionAdapter
70 | ) : Filter() {
71 | var mFilterTags: ArrayList? = null
72 | override fun performFiltering(constraint: CharSequence?): FilterResults {
73 | val results = FilterResults()
74 | if (mFilterTags == null) {
75 | mFilterTags = ArrayList(tags)
76 | }
77 | //如果没有过滤条件则不过滤
78 | if (constraint == null || constraint.isBlank()) {
79 | results.values = mFilterTags
80 | results.count = mFilterTags!!.size
81 | } else {
82 | val retList: MutableList = ArrayList()
83 | //过滤条件
84 | val str = constraint.toString()
85 | //循环变量数据源,如果有属性满足过滤条件,则添加到result中
86 | for (tag in mFilterTags!!) {
87 | if (PinyinUtil.getPinyin(tag, "").lowercase(Locale.getDefault()).contains(
88 | PinyinUtil.getPinyin(str, "").lowercase(Locale.getDefault())
89 | )
90 | ) {
91 | val chars: CharArray = str.toCharArray()
92 | var count = 0
93 | for (i in chars.indices) {
94 | // 判断是否为汉字字符
95 | if (chars[i].toString().matches(Regex("[\\u4E00-\\u9FA5]+"))) {
96 | count++
97 | }
98 | }
99 | if (count > 0) {
100 | if (tag.contains(str)) {
101 | retList.add(tag)
102 | }
103 | } else retList.add(tag)
104 | }
105 | }
106 | results.values = retList
107 | results.count = retList.size
108 | }
109 | return results
110 | }
111 |
112 | //在这里返回过滤结果
113 | override fun publishResults(
114 | constraint: CharSequence?,
115 | results: FilterResults
116 | ) {
117 | adapter.filterTags = (results.values as List<*>).filterIsInstance()
118 | if (results.count > 0) {
119 | adapter.notifyDataSetChanged()
120 | } else {
121 | adapter.notifyDataSetInvalidated()
122 | }
123 | }
124 | }
125 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/main/model/TagSelectedItem.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.main.model
2 |
3 | import androidx.databinding.DataBindingUtil
4 | import androidx.lifecycle.viewModelScope
5 | import cn.umafan.lib.android.R
6 | import cn.umafan.lib.android.databinding.ItemSelectedTagBinding
7 | import cn.umafan.lib.android.model.db.Tag
8 | import cn.umafan.lib.android.ui.main.MainViewModel
9 | import com.angcyo.dsladapter.DslAdapterItem
10 | import com.angcyo.dsladapter.DslViewHolder
11 | import kotlinx.coroutines.launch
12 |
13 |
14 | class TagSelectedItem(
15 | val tag: Tag,
16 | private val mViewModel: MainViewModel,
17 | private val flag: Boolean
18 | ) : DslAdapterItem() {
19 | override var itemLayoutId = R.layout.item_selected_tag
20 |
21 | init {
22 | itemData = tag
23 | thisAreContentsTheSame = { fromItem, newItem, _, _ ->
24 | fromItem?.itemData == newItem.itemData
25 | }
26 | thisAreItemsTheSame = { fromItem, newItem, _, _ ->
27 | fromItem?.itemData == newItem.itemData
28 | }
29 | }
30 |
31 | override fun onItemBind(
32 | itemHolder: DslViewHolder,
33 | itemPosition: Int,
34 | adapterItem: DslAdapterItem,
35 | payloads: List
36 | ) {
37 | super.onItemBind(itemHolder, itemPosition, adapterItem, payloads)
38 | itemHolder.view(R.id.item_selected_tag)?.let {
39 | DataBindingUtil.bind(it)?.apply {
40 | itemSelectedTagName.text = tag.name
41 | itemSelectedTagCancel.setOnClickListener {
42 | with(mViewModel) {
43 | viewModelScope.launch {
44 | if (flag) {
45 | searchParams.value?.tags?.remove(tag)
46 | val tmp = mutableSetOf()
47 | searchParams.value?.tags?.forEach { tag ->
48 | tmp.add(tag)
49 | }
50 | selectedTags.emit(tmp)
51 | } else {
52 | searchParams.value?.exceptedTags?.remove(tag)
53 | val tmp = mutableSetOf()
54 | searchParams.value?.exceptedTags?.forEach { tag ->
55 | tmp.add(tag)
56 | }
57 | selectedExceptTags.emit(tmp)
58 | }
59 | }
60 | }
61 | }
62 | invalidateAll()
63 | }
64 | }
65 | }
66 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/main/model/TagSuggestionAdapter.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.main.model
2 |
3 | import android.view.LayoutInflater
4 | import android.view.View
5 | import android.view.ViewGroup
6 | import android.widget.BaseAdapter
7 | import android.widget.Filter
8 | import android.widget.Filterable
9 | import cn.umafan.lib.android.R
10 | import cn.umafan.lib.android.model.MyApplication
11 | import cn.umafan.lib.android.model.db.Tag
12 | import cn.umafan.lib.android.util.PinyinUtil
13 | import com.google.android.material.textview.MaterialTextView
14 | import java.util.Locale
15 |
16 |
17 | /**
18 | * 用于搜索展示tag的适配器
19 | */
20 | class TagSuggestionAdapter(
21 | private val tags: List
22 | ) : BaseAdapter(), Filterable {
23 |
24 | private var mArrayFilter: ArrayFilter? = null
25 | private var filterTags: List = tags
26 |
27 | override fun getFilter(): Filter {
28 | if (null == mArrayFilter) {
29 | mArrayFilter = ArrayFilter(tags, this)
30 | }
31 | return mArrayFilter!!
32 | }
33 |
34 | override fun getItem(p0: Int): Any {
35 | return filterTags[p0]
36 | }
37 |
38 | override fun getItemId(p0: Int): Long {
39 | return p0.toLong()
40 | }
41 |
42 | override fun getCount(): Int {
43 | return filterTags.size
44 | }
45 |
46 | override fun getView(positon: Int, convertView: View?, parent: ViewGroup?): View {
47 | val viewHolder: ViewHolder?
48 | var mConvertView: View? = convertView
49 | if (null == mConvertView) {
50 | viewHolder = ViewHolder()
51 | mConvertView = LayoutInflater.from(MyApplication.context)
52 | .inflate(R.layout.item_tag_suggestion, null)
53 | viewHolder.tagName = mConvertView.findViewById(R.id.item_tag_suggestion_name)
54 | mConvertView.tag = viewHolder
55 | } else {
56 | viewHolder = mConvertView.tag as ViewHolder
57 | }
58 | val tag = filterTags[positon]
59 | viewHolder.tagName?.text = tag.name
60 |
61 | return mConvertView!!
62 | }
63 |
64 | class ViewHolder(
65 | var tagName: MaterialTextView? = null
66 | )
67 |
68 | private class ArrayFilter(
69 | private val tags: List,
70 | val adapter: TagSuggestionAdapter
71 | ) : Filter() {
72 | var mFilterTags: ArrayList? = null
73 | override fun performFiltering(constraint: CharSequence?): FilterResults {
74 | val results = FilterResults()
75 | if (mFilterTags == null) {
76 | mFilterTags = ArrayList(tags)
77 | }
78 | //如果没有过滤条件则不过滤
79 | if (constraint == null || constraint.isBlank()) {
80 | results.values = mFilterTags
81 | results.count = mFilterTags!!.size
82 | } else {
83 | val retList: MutableList = ArrayList()
84 | //过滤条件
85 | val str = constraint.toString()
86 | //循环变量数据源,如果有属性满足过滤条件,则添加到result中
87 | for (tag in mFilterTags!!) {
88 | val tagName = tag.name
89 | if (PinyinUtil.getPinyin(tagName, "").lowercase(Locale.getDefault()).contains(
90 | PinyinUtil.getPinyin(str, "").lowercase(Locale.getDefault())
91 | )
92 | ) {
93 | val chars: CharArray = str.toCharArray()
94 | var count = 0
95 | for (i in chars.indices) {
96 | // 判断是否为汉字字符
97 | if (chars[i].toString().matches(Regex("[\\u4E00-\\u9FA5]+"))) {
98 | count++
99 | }
100 | }
101 | if (count > 0) {
102 | if (tagName.contains(str)) {
103 | retList.add(tag)
104 | }
105 | } else retList.add(tag)
106 | }
107 | }
108 | results.values = retList
109 | results.count = retList.size
110 | }
111 | return results
112 | }
113 |
114 | //在这里返回过滤结果
115 | override fun publishResults(
116 | constraint: CharSequence?,
117 | results: FilterResults
118 | ) {
119 | adapter.filterTags = (results.values as List<*>).filterIsInstance()
120 | if (results.count > 0) {
121 | adapter.notifyDataSetChanged()
122 | } else {
123 | adapter.notifyDataSetInvalidated()
124 | }
125 | }
126 | }
127 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/reader/ReaderViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.reader
2 |
3 | import androidx.lifecycle.MutableLiveData
4 | import androidx.lifecycle.ViewModel
5 | import cn.umafan.lib.android.util.ReaderSettingUtil
6 |
7 |
8 | /**
9 | * @ClassName ReaderViewModel
10 | * @author Forever-DdB everddb@gmail.com
11 | * @Description
12 | * @createTime 2022年 08月07日 19:36
13 | **/
14 | class ReaderViewModel : ViewModel() {
15 | val collected = MutableLiveData(true)
16 | val fontSize: MutableLiveData
17 | val segmentSpace: MutableLiveData
18 |
19 | init {
20 | val setting = ReaderSettingUtil.getSetting("default")
21 | fontSize = MutableLiveData(setting.getString("fontSize"))
22 | segmentSpace = MutableLiveData(setting.getString("segmentSpace"))
23 | }
24 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/reader/model/ReaderJSInterface.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.reader.model
2 |
3 | import android.text.TextUtils
4 | import android.util.Log
5 | import cn.umafan.lib.android.R
6 | import cn.umafan.lib.android.model.db.Article
7 | import cn.umafan.lib.android.util.ReaderSettingUtil
8 | import cn.umafan.lib.android.util.SettingUtil
9 | import com.itheima.view.BridgeWebView
10 | import org.json.JSONArray
11 | import org.json.JSONObject
12 | import java.text.SimpleDateFormat
13 | import java.util.*
14 |
15 | class ReaderJSInterface(
16 | private val webView: BridgeWebView,
17 | private val article: Article
18 | ) {
19 |
20 | companion object {
21 | val FORMATTER = SimpleDateFormat(
22 | "yyyy-MM-dd HH:mm",
23 | Locale.CHINA
24 | )
25 |
26 | fun initiativeStyle(webView: BridgeWebView) {
27 | val callback = "renderAct"
28 |
29 | val json = JSONObject()
30 | json.put("setting", ReaderSettingUtil.getSetting("default"))
31 |
32 | if (TextUtils.isEmpty(callback)) return
33 | //调用js方法必须在主线程
34 | webView.post { webView.loadUrl("javascript:$callback($json)") }
35 | }
36 | }
37 |
38 | // 以此格式写方法
39 | fun getArticle(str: Array) {
40 | Log.d(this.javaClass.simpleName, str[0])
41 | //解析js callback方法
42 | val mJson = JSONObject(str[0])
43 | val callback = mJson.optString("callback") //解析js回调方法
44 |
45 | val json = JSONObject()
46 | json.put("name", article.name)
47 | json.put("source", article.source)
48 | json.put("note", article.note)
49 | json.put("translator", article.translator)
50 | json.put("author", article.author)
51 |
52 | var content = article.content
53 | val regex = Regex("\\[[^]]+][^<]*
\$")
54 | val elTagRegex = Regex("<[^>]*>")
55 | val dict = mutableMapOf()
56 | var result: MatchResult?
57 | do {
58 | result = regex.find(content)
59 | if (null != result) {
60 | content = content.substring(0, result.range.first)
61 | val annotationArr =
62 | result.value.replace(elTagRegex, "").split(']')
63 | val value = annotationArr[annotationArr.size - 1]
64 | annotationArr
65 | .filterIndexed { i, _ -> i != annotationArr.size - 1 }
66 | .forEach { key ->
67 | dict[key.substring(1)] = value
68 | }
69 | }
70 | } while (null != result)
71 | if (dict.isNotEmpty()) {
72 | val emptyElRegex = Regex("\\s*
\\s*
")
73 | content = content.replace(emptyElRegex, "")
74 | dict.forEach { (key, value) ->
75 | content = content.replace(
76 | "[$key]",
77 | " [" +
79 | "$key]$value "
80 | )
81 | }
82 | }
83 | json.put("content", content)
84 |
85 |
86 | val tagList = JSONArray(article.taggedList.sortedWith { a, b ->
87 | if (a.tag.type == b.tag.type)
88 | a.tag.name.compareTo(b.tag.name)
89 | else
90 | b.tag.type.compareTo(a.tag.type)
91 | }.map { tagged ->
92 | val tagJson = JSONObject()
93 | tagJson.put("name", tagged.tag.name)
94 | tagJson.put("type", tagged.tag.type)
95 | tagJson
96 | })
97 | json.put("tags", tagList)
98 | json.put(
99 | "time",
100 | FORMATTER.format(article.uploadTime.toLong() * 1000)
101 | )
102 | json.put("setting", ReaderSettingUtil.getSetting("default"))
103 | Log.d(this.javaClass.simpleName, SettingUtil.getTheme().toString())
104 | json.getJSONObject("setting").put(
105 | "theme", when (SettingUtil.getTheme()) {
106 | R.style.Theme_UmaLibrary_NGA -> "nga"
107 | R.style.Theme_UmaLibrary_WHITE -> "white"
108 | R.style.Theme_UmaLibrary_TEAL -> "cyan"
109 | else -> "purple"
110 | }
111 | )
112 | Log.d(this.javaClass.simpleName, json.toString())
113 |
114 | invokeJavaScript(callback, json.toString())
115 | }
116 |
117 | /**
118 | * 统一管理所有android调用js方法
119 | *
120 | * @param callback js回调方法名
121 | * @param json 传递json数据
122 | */
123 | private fun invokeJavaScript(callback: String, json: String) {
124 | Log.d(this.javaClass.simpleName, "callbackName: $callback data: $json")
125 | if (TextUtils.isEmpty(callback)) return
126 | //调用js方法必须在主线程
127 | webView.post { webView.loadUrl("javascript:$callback($json)") }
128 | }
129 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/recommend/RecommendViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.recommend
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 | import androidx.lifecycle.viewModelScope
7 | import cn.umafan.lib.android.ui.main.MainActivity
8 | import cn.umafan.lib.android.ui.recommend.model.RecInfo
9 | import cn.umafan.lib.android.ui.recommend.model.RecTabItem
10 | import com.angcyo.dsladapter.DslAdapter
11 | import kotlinx.coroutines.flow.MutableStateFlow
12 | import kotlinx.coroutines.launch
13 |
14 | class RecommendViewModel : ViewModel() {
15 | private val _text = MutableLiveData().apply {
16 | value = "This is home Fragment"
17 | }
18 | val text: LiveData = _text
19 |
20 | private val recData = MutableStateFlow(listOf())
21 |
22 | val currentPage = MutableLiveData(1)
23 |
24 | val type = MutableLiveData(0)
25 |
26 | val recDataAdapter = DslAdapter()
27 |
28 | var notShowJumpButtonList = mutableListOf()
29 |
30 | var collapsedList = mutableListOf()
31 |
32 | lateinit var activity: MainActivity
33 |
34 | init {
35 | viewModelScope.launch {
36 | recData.collect {
37 | recDataAdapter.changeDataItems { adapterItems ->
38 | adapterItems.clear()
39 | it.forEach {
40 | adapterItems.add(RecTabItem(it, activity, this@RecommendViewModel))
41 | }
42 | }
43 | }
44 | }
45 | }
46 |
47 | /**
48 | * 异步加载推荐数据
49 | */
50 | fun loadRecs(list: List?) {
51 | viewModelScope.launch {
52 | var data = list
53 | if (null == data) {
54 | data = mutableListOf()
55 | }
56 | recData.emit(data)
57 | }
58 | }
59 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/recommend/model/RecCommentItem.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.recommend.model
2 |
3 | import androidx.databinding.DataBindingUtil
4 | import cn.umafan.lib.android.R
5 | import cn.umafan.lib.android.databinding.ItemRecCommentBinding
6 | import cn.umafan.lib.android.model.db.Rec
7 | import com.angcyo.dsladapter.DslAdapterItem
8 | import com.angcyo.dsladapter.DslViewHolder
9 |
10 |
11 | /**
12 | * @ClassName RecCommentItem
13 | * @author ForeverDdB 835236331@qq.com
14 | * @Description
15 | * @createTime 2023年 06月12日 15:34
16 | **/
17 | class RecCommentItem(
18 | private val recInfo: Rec
19 | ) : DslAdapterItem() {
20 | override var itemLayoutId = R.layout.item_rec_comment
21 |
22 | init {
23 | itemData = recInfo
24 | thisAreContentsTheSame = { fromItem, newItem, _, _ ->
25 | fromItem?.itemData == newItem.itemData
26 | }
27 | thisAreItemsTheSame = { fromItem, newItem, _, _ ->
28 | fromItem?.itemData == newItem.itemData
29 | }
30 | }
31 |
32 | override fun onItemBind(
33 | itemHolder: DslViewHolder,
34 | itemPosition: Int,
35 | adapterItem: DslAdapterItem,
36 | payloads: List
37 | ) {
38 | super.onItemBind(itemHolder, itemPosition, adapterItem, payloads)
39 | itemHolder.view(R.id.item_rec_comment)?.let {
40 | DataBindingUtil.bind(it)?.apply {
41 | recReason.text = recInfo.reason
42 | recAuthor.text = "—— ${recInfo.name}"
43 |
44 | invalidateAll()
45 | }
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/recommend/model/RecInfo.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.recommend.model
2 |
3 | import cn.umafan.lib.android.model.db.Rec
4 |
5 | class RecInfo(
6 | var data: MutableList
7 | ): Rec() {
8 | lateinit var rec: Rec
9 | var classType: Int = 0
10 | // 传入Rec数据, 生成RecInfo
11 | companion object {
12 | fun fromRec(rec: Rec, data: MutableList, type: Int): RecInfo {
13 | val recInfo = RecInfo(data)
14 |
15 | recInfo.classType = type
16 | recInfo.id = rec.id
17 | recInfo.name = rec.name
18 | recInfo.type = rec.type
19 | recInfo.r = rec.r
20 | recInfo.reason = rec.reason
21 | recInfo.title = rec.title
22 | recInfo.refId = rec.refId
23 | recInfo.others = rec.others
24 | recInfo.rec = rec
25 |
26 | return recInfo
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/recommend/model/RecJumpItem.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.recommend.model
2 |
3 | import android.util.Log
4 | import androidx.databinding.DataBindingUtil
5 | import cn.umafan.lib.android.R
6 | import cn.umafan.lib.android.databinding.ItemJumpButtonBinding
7 | import cn.umafan.lib.android.model.SearchBean
8 | import cn.umafan.lib.android.ui.main.MainActivity
9 | import com.angcyo.dsladapter.DslAdapterItem
10 | import com.angcyo.dsladapter.DslViewHolder
11 |
12 |
13 | /**
14 | * @ClassName RecJumpItem
15 | * @author ForeverDdB 835236331@qq.com
16 | * @Description
17 | * @createTime 2023年 06月12日 23:36
18 | **/
19 | class RecJumpItem(
20 | val name: String,
21 | val searchBean: SearchBean,
22 | val activity: MainActivity
23 | ) : DslAdapterItem() {
24 | override var itemLayoutId = R.layout.item_jump_button
25 | init {
26 | itemData = name
27 | thisAreContentsTheSame = { fromItem, newItem, _, _ ->
28 | fromItem?.itemData == newItem.itemData
29 | }
30 | thisAreItemsTheSame = { fromItem, newItem, _, _ ->
31 | fromItem?.itemData == newItem.itemData
32 | }
33 | }
34 |
35 | override fun onItemBind(
36 | itemHolder: DslViewHolder,
37 | itemPosition: Int,
38 | adapterItem: DslAdapterItem,
39 | payloads: List
40 | ) {
41 | super.onItemBind(itemHolder, itemPosition, adapterItem, payloads)
42 | itemHolder.view(R.id.item_jump_button)?.let {
43 | DataBindingUtil.bind(it)?.apply {
44 | jumpButton.text = name
45 | jumpButton.setOnClickListener {
46 | activity.searchByOption(searchBean)
47 | }
48 | invalidateAll()
49 | }
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/setting/SettingViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.setting
2 |
3 | import androidx.lifecycle.ViewModel
4 |
5 |
6 | class SettingViewModel : ViewModel()
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/thanks/ThanksFragment.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.thanks
2 |
3 | import android.graphics.drawable.Drawable
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.view.View
7 | import android.view.ViewGroup
8 | import androidx.fragment.app.Fragment
9 | import cn.umafan.lib.android.databinding.FragmentThanksBinding
10 | import cn.umafan.lib.android.model.DataBaseHandler
11 | import cn.umafan.lib.android.model.MyApplication
12 | import cn.umafan.lib.android.model.MyBaseActivity
13 | import cn.umafan.lib.android.model.db.DaoSession
14 | import cn.umafan.lib.android.ui.main.DatabaseCopyThread
15 | import cn.umafan.lib.android.util.SettingUtil
16 |
17 | class ThanksFragment : Fragment() {
18 | private var _binding: FragmentThanksBinding? = null
19 |
20 | private var daoSession: DaoSession? = null
21 |
22 | private val binding get() = _binding!!
23 |
24 | override fun onCreateView(
25 | inflater: LayoutInflater,
26 | container: ViewGroup?,
27 | savedInstanceState: Bundle?
28 | ): View {
29 | _binding = FragmentThanksBinding.inflate(inflater, container, false)
30 |
31 | with(binding) {
32 | appVersion.text = MyApplication.getVersion().name
33 | layout.apply {
34 | val uri = SettingUtil.getImageBackground(SettingUtil.INDEX_BG)
35 | if (null != uri) background = Drawable.createFromPath(uri.path)
36 | }
37 | }
38 |
39 | loadCreators()
40 |
41 | return binding.root
42 | }
43 |
44 | private fun loadCreators() {
45 | val handler = DataBaseHandler(activity as MyBaseActivity) {
46 | daoSession = it.obj as DaoSession
47 | if (null != daoSession) {
48 | val creatorDao = daoSession!!.creatorDao
49 | val creators = creatorDao.queryBuilder().build().listLazy().first().names
50 | binding.othersContent.text =
51 | creators.substring(2, creators.length - 2).replace("\",\"", " | ")
52 | }
53 | }
54 | DatabaseCopyThread.addHandler(handler)
55 | }
56 |
57 | override fun onDestroyView() {
58 | super.onDestroyView()
59 | _binding = null
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/ui/thanks/ThanksViewModel.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.ui.thanks
2 |
3 | import androidx.lifecycle.LiveData
4 | import androidx.lifecycle.MutableLiveData
5 | import androidx.lifecycle.ViewModel
6 |
7 | class ThanksViewModel : ViewModel() {
8 | private val _text = MutableLiveData().apply {
9 | value = "This is thanks Fragment"
10 | }
11 | val text: LiveData = _text
12 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/ContentUriUtil.java:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util;
2 |
3 | import android.content.ContentUris;
4 | import android.content.Context;
5 | import android.database.Cursor;
6 | import android.net.Uri;
7 | import android.os.Environment;
8 | import android.provider.DocumentsContract;
9 | import android.provider.MediaStore;
10 |
11 | public class ContentUriUtil {
12 | public static String getAbsolutePath(Context context, Uri imageUri) {
13 | if (context == null || imageUri == null)
14 | return null;
15 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, imageUri)) {
16 | if (isExternalStorageDocument(imageUri)) {
17 | String docId = DocumentsContract.getDocumentId(imageUri);
18 | String[] split = docId.split(":");
19 | String type = split[0];
20 | if ("primary".equalsIgnoreCase(type)) {
21 | return Environment.getExternalStorageDirectory() + "/" + split[1];
22 | }
23 | } else if (isDownloadsDocument(imageUri)) {
24 | String id = DocumentsContract.getDocumentId(imageUri);
25 | Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.parseLong(id));
26 | return getDataColumn(context, contentUri, null, null);
27 | } else if (isMediaDocument(imageUri)) {
28 | String docId = DocumentsContract.getDocumentId(imageUri);
29 | String[] split = docId.split(":");
30 | String type = split[0];
31 | Uri contentUri = null;
32 | if ("image".equals(type)) {
33 | contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
34 | } else if ("video".equals(type)) {
35 | contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
36 | } else if ("audio".equals(type)) {
37 | contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
38 | }
39 | String selection = MediaStore.Images.Media._ID + "=?";
40 | String[] selectionArgs = new String[]{split[1]};
41 | return getDataColumn(context, contentUri, selection, selectionArgs);
42 | }
43 | } // MediaStore (and general)
44 | else if ("content".equalsIgnoreCase(imageUri.getScheme())) {
45 | // Return the remote address
46 | if (isGooglePhotosUri(imageUri))
47 | return imageUri.getLastPathSegment();
48 | return getDataColumn(context, imageUri, null, null);
49 | }
50 | // File
51 | else if ("file".equalsIgnoreCase(imageUri.getScheme())) {
52 | return imageUri.getPath();
53 | }
54 | return null;
55 | }
56 |
57 | /**
58 | * 从本地设备数据库查询数据.
59 | *
60 | * @param context 上下文
61 | * @param uri 内容提供者的标识
62 | * @param selection 设置条件,相当于SQL语句中的where
63 | * @param selectionArgs 条件值
64 | * @return 查询结果
65 | */
66 | public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
67 | Cursor cursor = null;
68 | String column = MediaStore.Images.Media.DATA;
69 | String[] projection = {column}; //告诉Provider要返回的内容(列Column)
70 | try {
71 | cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
72 | if (cursor != null && cursor.moveToFirst()) {
73 | int index = cursor.getColumnIndexOrThrow(column);
74 | return cursor.getString(index);
75 | }
76 | } finally {
77 | if (cursor != null)
78 | cursor.close();
79 | }
80 | return null;
81 | }
82 |
83 | /**
84 | * @param uri The Uri to check.
85 | * @return Whether the Uri authority is ExternalStorageProvider.
86 | */
87 | public static boolean isExternalStorageDocument(Uri uri) {
88 | return "com.android.externalstorage.documents".equals(uri.getAuthority());
89 | }
90 |
91 | /**
92 | * @param uri The Uri to check.
93 | * @return Whether the Uri authority is DownloadsProvider.
94 | */
95 | public static boolean isDownloadsDocument(Uri uri) {
96 | return "com.android.providers.downloads.documents".equals(uri.getAuthority());
97 | }
98 |
99 | /**
100 | * @param uri The Uri to check.
101 | * @return Whether the Uri authority is MediaProvider.
102 | */
103 | public static boolean isMediaDocument(Uri uri) {
104 | return "com.android.providers.media.documents".equals(uri.getAuthority());
105 | }
106 |
107 | /**
108 | * @param uri The Uri to check.
109 | * @return Whether the Uri authority is Google Photos.
110 | */
111 | public static boolean isGooglePhotosUri(Uri uri) {
112 | return "com.google.android.apps.photos.content".equals(uri.getAuthority());
113 | }
114 | }
115 |
116 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/HistoryUtil.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util
2 |
3 | import android.content.SharedPreferences
4 | import cn.umafan.lib.android.model.MyApplication
5 | import cn.umafan.lib.android.model.db.Article
6 | import org.json.JSONArray
7 | import org.json.JSONObject
8 |
9 | object HistoryUtil {
10 | private const val fileName = "history"
11 | private const val maxLength = 20
12 |
13 | private var sharedPreferences: SharedPreferences =
14 | MyApplication.context.getSharedPreferences(fileName, 0)
15 |
16 | /**
17 | * 保存到历史记录
18 | */
19 | fun saveHistory(article: Article): Boolean {
20 | return try {
21 | val editor = sharedPreferences.edit()
22 | val originData = JSONArray(sharedPreferences.getString(fileName, "[]")!!)
23 | for (i in 0 until originData.length()) {
24 | if (originData.getJSONObject(i).getInt("id") == article.id.toInt()) {
25 | val data = originData.getJSONObject(i)
26 | originData.remove(i)
27 | originData.put(data)
28 | editor.putString(fileName, originData.toString())
29 | editor.apply()
30 | return true
31 | }
32 | }
33 | if (originData.length() >= maxLength) {
34 | originData.remove(0)
35 | }
36 | val data = JSONObject()
37 | data.put("id", article.id.toInt())
38 | data.put("name", article.name)
39 | data.put("author", article.author)
40 | data.put("translator", article.translator)
41 | val tagList = JSONArray(article.taggedList.sortedWith { a, b ->
42 | if (a.tag.type == b.tag.type)
43 | a.tag.name.compareTo(b.tag.name)
44 | else
45 | b.tag.type.compareTo(a.tag.type)
46 | }.map { tagged -> tagged.tag.name })
47 | data.put("tags", tagList)
48 | originData.put(data)
49 | editor.putString(fileName, originData.toString())
50 | editor.apply()
51 | true
52 | } catch (e: Exception) {
53 | e.printStackTrace()
54 | false
55 | }
56 | }
57 |
58 | /**
59 | * 获取历史记录
60 | */
61 | fun getHistory(): List {
62 | val originData = JSONArray(sharedPreferences.getString(fileName, "[]"))
63 | val data = mutableListOf()
64 | for (i in 0 until originData.length()) {
65 | data.add(originData.getJSONObject(originData.length() - i - 1).getInt("id"))
66 | }
67 | return data
68 | }
69 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/PageSizeUtil.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util
2 |
3 | import android.content.SharedPreferences
4 | import cn.umafan.lib.android.model.MyApplication
5 |
6 |
7 | object PageSizeUtil {
8 | private const val fileName = "page_size"
9 |
10 | private var sharedPreferences: SharedPreferences =
11 | MyApplication.context.getSharedPreferences(fileName, 0)
12 |
13 | fun getSize(): Int {
14 | return sharedPreferences.getInt(
15 | "size",
16 | 20
17 | )
18 | }
19 |
20 | fun setSize(size: Int): Boolean {
21 | return try {
22 | sharedPreferences.edit().putInt("size", size).apply()
23 | true
24 | } catch (e: Exception) {
25 | e.printStackTrace()
26 | false
27 | }
28 | }
29 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/PinyinUtil.java:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util;
2 |
3 |
4 | import net.sourceforge.pinyin4j.PinyinHelper;
5 | import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType;
6 | import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat;
7 | import net.sourceforge.pinyin4j.format.HanyuPinyinToneType;
8 | import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType;
9 | import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination;
10 |
11 | public class PinyinUtil {
12 |
13 | /**
14 | * 将汉字转换为全拼
15 | *
16 | * @param text 文本
17 | * @param separator 分隔符
18 | * @return {@link String}
19 | */
20 | public static String getPinyin(String text, String separator) {
21 | char[] chars = text.toCharArray();
22 | HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat();
23 | // 设置大小写
24 | format.setCaseType(HanyuPinyinCaseType.LOWERCASE);
25 | // 设置声调表示方法
26 | format.setToneType(HanyuPinyinToneType.WITHOUT_TONE);
27 | // 设置字母u表示方法
28 | format.setVCharType(HanyuPinyinVCharType.WITH_V);
29 | String[] s;
30 | String rs = "";
31 | try {
32 | StringBuilder sb = new StringBuilder();
33 | for (int i = 0; i < chars.length; i++) {
34 | // 判断是否为汉字字符
35 | if (String.valueOf(chars[i]).matches("[\\u4E00-\\u9FA5]+")) {
36 | s = PinyinHelper.toHanyuPinyinStringArray(chars[i], format);
37 | if (s != null) {
38 | sb.append(s[0]).append(separator);
39 | continue;
40 | }
41 | }
42 | sb.append(chars[i]);
43 | if ((i + 1 >= chars.length) || String.valueOf(chars[i + 1]).matches("[\\u4E00-\\u9FA5]+")) {
44 | sb.append(separator);
45 | }
46 | }
47 | rs = sb.substring(0, sb.length());
48 | } catch (BadHanyuPinyinOutputFormatCombination e) {
49 | e.printStackTrace();
50 | }
51 | return rs;
52 | }
53 |
54 | /**
55 | * 获取汉字首字母
56 | *
57 | * @param text 文本
58 | * @return {@link String}
59 | */
60 | public static String getPinyinInitials(String text) {
61 | StringBuilder sb = new StringBuilder();
62 | for (int i = 0; i < text.length(); i++) {
63 | char ch = text.charAt(i);
64 | String[] s = PinyinHelper.toHanyuPinyinStringArray(ch);
65 | if (s != null) {
66 | sb.append(s[0].charAt(0));
67 | } else {
68 | sb.append(ch);
69 | }
70 | }
71 | return sb.toString();
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/ReaderSettingUtil.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util
2 |
3 | import android.content.SharedPreferences
4 | import cn.umafan.lib.android.model.MyApplication
5 | import org.json.JSONObject
6 |
7 |
8 | object ReaderSettingUtil {
9 |
10 | private const val fileName = "reader_setting"
11 |
12 | private var sharedPreferences: SharedPreferences =
13 | MyApplication.context.getSharedPreferences(fileName, 0)
14 |
15 | fun getSetting(theme: String): JSONObject {
16 | return JSONObject(
17 | sharedPreferences.getString(
18 | theme,
19 | "{\"fontSize\": \"normal\", \"segmentSpace\": \"wider\"}"
20 | )!!
21 | )
22 | }
23 |
24 | fun setSetting(theme: String, fontSize: String, segmentSpace: String): Boolean {
25 | val map = mutableMapOf(Pair("fontSize", fontSize), Pair("segmentSpace", segmentSpace))
26 | val json = (map as Map<*, *>?)?.let { JSONObject(it) }
27 | return try {
28 | sharedPreferences.edit().putString(theme, json.toString()).apply()
29 | true
30 | } catch (e: Exception) {
31 | e.printStackTrace()
32 | false
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/ZipUtil.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util
2 |
3 | import java.io.BufferedInputStream
4 | import java.io.BufferedOutputStream
5 | import java.io.File
6 | import java.io.FileOutputStream
7 | import java.util.*
8 | import java.util.zip.ZipEntry
9 | import java.util.zip.ZipFile
10 |
11 |
12 | /**
13 | * @author HY
14 | */
15 | object ZipUtil {
16 | private const val BUFFER = 1024
17 |
18 | fun unzip(filePath: String?, zipDir: String): String {
19 | var name = ""
20 | try {
21 | var bufferedOutputStream: BufferedOutputStream?
22 | var bufferedInputStream: BufferedInputStream?
23 | var entry: ZipEntry
24 | val zipFile = ZipFile(filePath)
25 | val dir: Enumeration<*> = zipFile.entries()
26 | while (dir.hasMoreElements()) {
27 | entry = dir.nextElement() as ZipEntry
28 | if (entry.isDirectory) {
29 | name = entry.name
30 | name = name.substring(0, name.length - 1)
31 | val fileObject = File(zipDir + name)
32 | fileObject.mkdir()
33 | }
34 | name = entry.name
35 | }
36 | val e: Enumeration<*> = zipFile.entries()
37 | while (e.hasMoreElements()) {
38 | entry = e.nextElement() as ZipEntry
39 | if (entry.isDirectory) {
40 | continue
41 | } else {
42 | bufferedInputStream = BufferedInputStream(zipFile.getInputStream(entry))
43 | var count: Int
44 | val dataByte = ByteArray(BUFFER)
45 | val fos = FileOutputStream(zipDir + entry.name)
46 | bufferedOutputStream = BufferedOutputStream(fos, BUFFER)
47 | while (
48 | bufferedInputStream.read(dataByte, 0, BUFFER)
49 | .also { count = it } != -1
50 | ) {
51 | bufferedOutputStream.write(dataByte, 0, count)
52 | }
53 | bufferedOutputStream.flush()
54 | bufferedOutputStream.close()
55 | bufferedInputStream.close()
56 | }
57 | }
58 | } catch (e: Exception) {
59 | e.printStackTrace()
60 | }
61 | return name
62 | }
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/network/ServiceCreator.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util.network
2 |
3 | import android.annotation.SuppressLint
4 | import okhttp3.OkHttpClient
5 | import retrofit2.Retrofit
6 | import retrofit2.converter.gson.GsonConverterFactory
7 | import java.net.Proxy
8 | import java.security.SecureRandom
9 | import java.security.cert.*
10 | import javax.net.ssl.SSLContext
11 | import javax.net.ssl.SSLSocketFactory
12 | import javax.net.ssl.TrustManager
13 | import javax.net.ssl.X509TrustManager
14 |
15 |
16 | object ServiceCreator {
17 |
18 | private fun createSSLSocketFactory(): SSLSocketFactory? {
19 | var ssfFactory: SSLSocketFactory? = null
20 | try {
21 | val mMyTrustManager = MyTrustManager()
22 | val sc = SSLContext.getInstance("TLS")
23 | sc.init(null, arrayOf(mMyTrustManager), SecureRandom())
24 | ssfFactory = sc.socketFactory
25 | } catch (ignored: Exception) {
26 | ignored.printStackTrace()
27 | }
28 | return ssfFactory
29 | }
30 |
31 | //实现X509TrustManager接口
32 | @SuppressLint("CustomX509TrustManager")
33 | class MyTrustManager : X509TrustManager {
34 | @SuppressLint("TrustAllX509TrustManager")
35 | @Throws(CertificateException::class)
36 | override fun checkClientTrusted(chain: Array?, authType: String?) {}
37 | @SuppressLint("TrustAllX509TrustManager")
38 | @Throws(CertificateException::class)
39 | override fun checkServerTrusted(chain: Array?, authType: String?) {}
40 |
41 | override fun getAcceptedIssuers(): Array {
42 | return arrayOf()
43 | }
44 | }
45 |
46 |
47 | private const val BASE_URL = "https://umalib.gitgud.site/"
48 |
49 | private val okHttpClient = OkHttpClient.Builder()
50 | .proxy(Proxy.NO_PROXY)
51 | .sslSocketFactory(createSSLSocketFactory()!!, MyTrustManager())
52 | .build()
53 |
54 | private val retrofit: Retrofit = Retrofit.Builder()
55 | .baseUrl(BASE_URL)
56 | .client(okHttpClient)
57 | .addConverterFactory(GsonConverterFactory.create())
58 | .build()
59 |
60 | fun create(serviceClass: Class): T = retrofit.create(serviceClass)
61 |
62 | inline fun create(): T = create(T::class.java)
63 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/network/UpdateUtil.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util.network
2 |
3 | import cn.umafan.lib.android.util.network.service.UpdateService
4 | import kotlinx.coroutines.Dispatchers
5 | import kotlinx.coroutines.withContext
6 |
7 | typealias MyHeaderMap = MutableMap
8 |
9 | fun getHeaderMap(): MyHeaderMap = mutableMapOf(
10 | Pair("Content-type", "application/json")
11 | )
12 |
13 | object UpdateUtil {
14 | private val updateService by lazy {
15 | ServiceCreator.create()
16 | }
17 |
18 | suspend fun getUpdate() = catchError {
19 | updateService.getUpdate(getHeaderMap())
20 | }
21 |
22 | /**
23 | * 所有的网络请求都统一经过这个函数
24 | */
25 | private suspend fun catchError(block: suspend () -> T): T? {
26 | return try {
27 | val result = withContext(Dispatchers.IO) { block() }
28 | result
29 | } catch (e: Exception) {
30 | e.printStackTrace()
31 | if (e is retrofit2.HttpException) {
32 | null
33 | } else {
34 | null
35 | }
36 | }
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/network/model/UpdateBean.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util.network.model
2 |
3 | data class UpdateBean(
4 | val button: UpdateButtonBean,
5 | val currentVersionName: String = "",
6 | val currentVersion: Int = 0,
7 | val info: UpdateInfoBean,
8 | var show: Int = 0,
9 | var initiative: Boolean = false,
10 | var currentDb: Int = 0,
11 | var downloadUrl: String = ""
12 | )
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/network/model/UpdateButtonBean.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util.network.model
2 |
3 | data class UpdateButtonBean(
4 | val text: String = "",
5 | val url: String = ""
6 | )
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/network/model/UpdateInfoBean.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util.network.model
2 |
3 | data class UpdateInfoBean(
4 | val title: String = "",
5 | val message: String = ""
6 | )
--------------------------------------------------------------------------------
/app/src/main/java/cn/umafan/lib/android/util/network/service/UpdateService.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android.util.network.service
2 |
3 | import cn.umafan.lib.android.util.network.MyHeaderMap
4 | import cn.umafan.lib.android.util.network.model.UpdateBean
5 | import retrofit2.http.GET
6 | import retrofit2.http.HeaderMap
7 |
8 |
9 | interface UpdateService {
10 |
11 | @GET("/UmaLibAndroid/update-info.json")
12 | suspend fun getUpdate(
13 | @HeaderMap headerMap: MyHeaderMap
14 | ): UpdateBean
15 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_github.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/app_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/drawable/app_bg.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/app_database_intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/drawable/app_database_intro.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/app_func_intro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/drawable/app_func_intro.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/app_tag_intro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/drawable/app_tag_intro.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/app_view_intro.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/drawable/app_view_intro.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_arrow_right_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_help_outline_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_menu_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_menu_open_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/baseline_volunteer_activism_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
16 |
17 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_article_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_cancel_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_chat_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_cloud_download_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_cloud_upload_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_collections_bookmark_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_color_lens_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_filter_alt_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_history_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_insert_photo_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_navigate_before_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_navigate_next_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_recommend_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_refresh_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_search_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_settings_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_star_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_star_outline_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_baseline_system_update_24.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/drawable/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_lightbulb_24px.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/index_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/drawable/index_bg.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/kitasan.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/drawable/kitasan.jpg
--------------------------------------------------------------------------------
/app/src/main/res/drawable/normal_bg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/drawable/normal_bg.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/tokaiteio.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/drawable/tokaiteio.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
17 |
18 |
26 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main_intro.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_reader.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
24 |
25 |
29 |
30 |
35 |
36 |
47 |
48 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_update_log.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
24 |
25 |
26 |
27 |
31 |
32 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_bar_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
25 |
26 |
34 |
35 |
41 |
42 |
43 |
44 |
45 |
53 |
54 |
57 |
58 |
67 |
68 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_loading_database.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
17 |
18 |
29 |
30 |
41 |
42 |
51 |
52 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_page_selector.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
23 |
24 |
31 |
32 |
33 |
34 |
47 |
48 |
53 |
54 |
55 |
56 |
57 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_reader_setting.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
16 |
17 |
24 |
25 |
32 |
33 |
34 |
35 |
43 |
44 |
57 |
58 |
66 |
67 |
80 |
81 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_collections.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_favorites.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
30 |
31 |
43 |
44 |
56 |
57 |
69 |
70 |
89 |
90 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_histroy.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
30 |
31 |
43 |
44 |
56 |
57 |
76 |
77 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_recommend.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
17 |
18 |
22 |
23 |
27 |
28 |
32 |
33 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
56 |
57 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_jump_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
14 |
15 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_page_button.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
31 |
32 |
36 |
37 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_rec_comment.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
14 |
32 |
33 |
36 |
37 |
51 |
52 |
68 |
69 |
70 |
71 |
72 |
73 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_selected_tag.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
13 |
14 |
30 |
31 |
35 |
36 |
46 |
47 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/item_tag_suggestion.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
14 |
15 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/nav_header_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
11 |
12 |
20 |
21 |
28 |
29 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_main_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/app/src/main/res/navigation/mobile_navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
25 |
26 |
31 |
32 |
37 |
38 |
44 |
45 |
--------------------------------------------------------------------------------
/app/src/main/res/values-land/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 | 48dp
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w1240dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 200dp
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w600dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 48dp
3 | 48dp
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #1FA3A2
8 | #FF018786
9 | #FF000000
10 | #FFFFFFFF
11 | #AAAAAA
12 | #DDDDDD
13 | #f3e3fc
14 | #342c3c
15 | #03A9F4
16 | #cc7ede
17 |
18 | #d6cab5
19 | #1c1c24
20 | #3b2620
21 | #bfb3ac
22 | #b1b4b0
23 | #be914d
24 | #faf9e1
25 | #332807
26 |
27 | #fbfbfb
28 | #DDDDDD
29 | #BBBBBB
30 | #cae2ec
31 | #98b2d5
32 | #5d98e8
33 | #999999
34 | #AAAAAA
35 |
36 | #cbeaac
37 | #57a86c
38 | #608d8a
39 | #cf8045
40 | #e69048
41 | #dcb26e
42 | #C4F6DA
43 | #c17949
44 | #206C32
45 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 180dp
3 | 16dp
4 | 16dp
5 |
6 | 16dp
7 | 16dp
8 | 8dp
9 | 176dp
10 | 35dp
11 | 150dp
12 | 150dp
13 | 120dp
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/shape.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/file_paths.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/test/java/cn/umafan/lib/android/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package cn.umafan.lib.android
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 |
6 | /**
7 | * Example local unit test, which will execute on the development machine (host).
8 | *
9 | * See [testing documentation](http://d.android.com/tools/testing).
10 | */
11 | class ExampleUnitTest {
12 | @Test
13 | fun addition_isCorrect() {
14 | assertEquals(4, 2 + 2)
15 | }
16 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | maven { url 'https://jitpack.io' }
6 | maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
7 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
8 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
9 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/gradle-plugin' }
10 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
11 | maven { url "https://raw.githubusercontent.com/Pgyer/mvn_repo_pgyer/master" }
12 | maven { url "https://maven.admobile.top/repository/maven-releases/" }
13 | }
14 | dependencies {
15 | classpath 'com.android.tools.build:gradle:7.4.1'
16 | classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
17 | }
18 | }
19 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
20 | plugins {
21 | id 'com.android.application' version '7.4.1' apply false
22 | id 'com.android.library' version '7.4.1' apply false
23 | id 'org.jetbrains.kotlin.android' version '1.7.0' apply false
24 | }
25 |
26 | allprojects {
27 | repositories {
28 | google()
29 | mavenCentral()
30 | maven { url 'https://jitpack.io' }
31 | maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
32 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
33 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
34 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/gradle-plugin' }
35 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
36 | maven { url "https://raw.githubusercontent.com/Pgyer/mvn_repo_pgyer/master" }
37 | maven { url "https://maven.admobile.top/repository/maven-releases/" }
38 | }
39 | }
40 |
41 | task clean(type: Delete) {
42 | delete rootProject.buildDir
43 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
24 | android.enableJetifier=true
25 | android.injected.testOnly=false
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri Jul 29 02:21:05 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.5-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/preference/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/preference/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'kotlin-android'
4 | }
5 |
6 | android {
7 | compileSdkVersion 31
8 |
9 | defaultConfig {
10 | minSdkVersion 21
11 | targetSdkVersion 31
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles "consumer-rules.pro"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | targetCompatibility JavaVersion.VERSION_1_8
26 | }
27 | kotlinOptions {
28 | jvmTarget = '1.8'
29 | }
30 | }
31 |
32 | dependencies {
33 | implementation 'com.github.ldh-star:EasyingContext:1.0.4'
34 | implementation 'androidx.preference:preference:1.2.0-rc01'
35 | implementation 'com.google.android.material:material:1.6.0-alpha02'
36 | }
--------------------------------------------------------------------------------
/preference/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/umalib/UmaLibAndroid/f3ee03901c0e20469fdd4e2e7ca696b7c0e71f7f/preference/consumer-rules.pro
--------------------------------------------------------------------------------
/preference/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
--------------------------------------------------------------------------------
/preference/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/preference/src/main/java/com/liangguo/preference/views/CommonSettingView.kt:
--------------------------------------------------------------------------------
1 | package com.liangguo.preference.views
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.Context
5 | import android.util.AttributeSet
6 | import android.view.LayoutInflater
7 | import android.widget.FrameLayout
8 | import androidx.appcompat.widget.AppCompatImageView
9 | import androidx.core.view.isVisible
10 | import com.google.android.material.textview.MaterialTextView
11 | import com.liangguo.preference.R
12 |
13 |
14 | /**
15 | * @author ldh
16 | * 时间: 2022/3/17 14:16
17 | *
18 | */
19 | @SuppressLint("CustomViewStyleable")
20 | class CommonSettingView @JvmOverloads constructor(
21 | context: Context,
22 | attrs: AttributeSet? = null,
23 | defStyleAttr: Int = -1
24 | ) : FrameLayout(context, attrs, defStyleAttr) {
25 |
26 | val titleTextView by lazy {
27 | findViewById(android.R.id.title)
28 | }
29 |
30 | val subtitleTextView by lazy {
31 | findViewById(android.R.id.summary)
32 | }
33 |
34 | val endTextView by lazy {
35 | findViewById(R.id.textView_end)
36 | }
37 |
38 | val iconView by lazy {
39 | findViewById(android.R.id.icon)
40 | }
41 |
42 | val endIconView by lazy {
43 | findViewById(R.id.imageView_right_icon)
44 | }
45 |
46 | init {
47 | LayoutInflater.from(context).inflate(R.layout.item_common_preference, this, true)
48 | context.obtainStyledAttributes(attrs, R.styleable.CommonSettingsView, defStyleAttr, 0)
49 | .apply {
50 | titleTextView.text = getText(R.styleable.CommonSettingsView_title)
51 | subtitleTextView.text = getText(R.styleable.CommonSettingsView_subtitle)
52 | endTextView.text = getText(R.styleable.CommonSettingsView_end_text)
53 | endIconView.isVisible = getBoolean(R.styleable.CommonSettingsView_showEndIcon, true)
54 | iconView.setImageDrawable(getDrawable(R.styleable.CommonSettingsView_icon))
55 | }.recycle()
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/preference/src/main/res/drawable/ic_baseline_keyboard_arrow_right_24.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/preference/src/main/res/drawable/switch_thumb.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
8 |
9 |
10 |
11 |
12 |
13 | -
14 |
15 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/preference/src/main/res/drawable/switch_track.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | -
4 |
5 |
6 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/preference/src/main/res/layout/item_common_preference.xml:
--------------------------------------------------------------------------------
1 |
2 |
16 |
17 |
24 |
25 |
39 |
40 |
55 |
56 |
62 |
63 |
71 |
72 |
86 |
87 |
88 |
89 |
90 |
100 |
101 |
--------------------------------------------------------------------------------
/preference/src/main/res/values/attrs_baseline_textview.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/preference/src/main/res/values/attrs_common_setting_view.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/preference/src/main/res/values/attrs_pref_common.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/preference/src/main/res/values/attrs_pref_list.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/preference/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 56dp
4 |
--------------------------------------------------------------------------------
/preference/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 确定
4 | 取消
5 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | maven { url 'https://jitpack.io' }
7 | maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
8 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
9 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
10 | maven { url 'https://maven.aliyun.com/nexus/content/repositories/gradle-plugin' }
11 | maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
12 | maven { url "https://raw.githubusercontent.com/Pgyer/mvn_repo_pgyer/master" }
13 | maven { url "https://maven.admobile.top/repository/maven-releases/" }
14 | }
15 | }
16 |
17 | //dependencyResolutionManagement {
18 | // repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
19 | // repositories {
20 | // google()
21 | // mavenCentral()
22 | // maven { url 'https://jitpack.io' }
23 | // maven { url 'https://maven.aliyun.com/nexus/content/groups/public/' }
24 | // maven { url 'https://maven.aliyun.com/nexus/content/repositories/jcenter' }
25 | // maven { url 'https://maven.aliyun.com/nexus/content/repositories/google' }
26 | // maven { url 'https://maven.aliyun.com/nexus/content/repositories/gradle-plugin' }
27 | // maven { url "https://oss.sonatype.org/content/repositories/snapshots/" }
28 | // maven { url "https://raw.githubusercontent.com/Pgyer/mvn_repo_pgyer/master" }
29 | // maven { url "https://maven.admobile.top/repository/maven-releases/" }
30 | // }
31 | //}
32 | rootProject.name = "UmaLibAndroid"
33 | include ':app'
34 | include ':preference'
35 |
--------------------------------------------------------------------------------